0001: /**
0002: * Copyright 2003-2007 Luck Consulting Pty Ltd
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */package net.sf.ehcache;
0016:
0017: import net.sf.ehcache.config.Configuration;
0018: import net.sf.ehcache.config.ConfigurationFactory;
0019: import net.sf.ehcache.config.ConfigurationHelper;
0020: import net.sf.ehcache.distribution.CacheManagerPeerListener;
0021: import net.sf.ehcache.distribution.CacheManagerPeerProvider;
0022: import net.sf.ehcache.event.CacheManagerEventListener;
0023: import net.sf.ehcache.event.CacheManagerEventListenerRegistry;
0024: import net.sf.ehcache.store.DiskStore;
0025: import net.sf.ehcache.util.PropertyUtil;
0026: import net.sf.ehcache.jcache.JCache;
0027: import org.apache.commons.logging.Log;
0028: import org.apache.commons.logging.LogFactory;
0029:
0030: import java.io.File;
0031: import java.io.InputStream;
0032: import java.net.URL;
0033: import java.util.ArrayList;
0034: import java.util.Collection;
0035: import java.util.Collections;
0036: import java.util.HashMap;
0037: import java.util.Iterator;
0038: import java.util.List;
0039: import java.util.Map;
0040: import java.util.Set;
0041:
0042: /**
0043: * A container for {@link Ehcache}s that maintain all aspects of their lifecycle.
0044: * <p/>
0045: * CacheManager may be either be a singleton if created with factory methods, or multiple instances may exist,
0046: * in which case resources required by each must be unique.
0047: * <p/>
0048: * A CacheManager holds references to Cache, Ehcache, and JCache objects and manages their creation and lifecycle.
0049: *
0050: * @author Greg Luck
0051: * @version $Id: CacheManager.java 571 2007-12-25 23:56:28Z gregluck $
0052: */
0053: public class CacheManager {
0054:
0055: /**
0056: * Keeps track of all known CacheManagers. Used to check on conflicts.
0057: * CacheManagers should remove themselves from this list during shut down.
0058: */
0059: public static final List ALL_CACHE_MANAGERS = Collections
0060: .synchronizedList(new ArrayList());
0061:
0062: /**
0063: * System property to enable creation of a shutdown hook for CacheManager.
0064: */
0065: public static final String ENABLE_SHUTDOWN_HOOK_PROPERTY = "net.sf.ehcache.enableShutdownHook";
0066:
0067: private static final Log LOG = LogFactory.getLog(CacheManager.class
0068: .getName());
0069:
0070: /**
0071: * The Singleton Instance.
0072: */
0073: private static CacheManager singleton;
0074:
0075: /**
0076: * Ehcaches managed by this manager.
0077: */
0078: protected final Map ehcaches = new HashMap();
0079:
0080: /**
0081: * Caches managed by this manager. A Cache is also an Ehcache.
0082: * For central managment the cache is also in the ehcaches map.
0083: */
0084: protected final Map caches = new HashMap();
0085:
0086: /**
0087: * JCaches managed by this manager. For each JCache there is a backing ehcache stored in the ehcaches map.
0088: */
0089: protected final Map jCaches = new HashMap();
0090:
0091: /**
0092: * Default cache cache.
0093: */
0094: private Ehcache defaultCache;
0095:
0096: /**
0097: * The path for the directory in which disk caches are created.
0098: */
0099: private String diskStorePath;
0100:
0101: /**
0102: * A name for this CacheManager to distinguish it from others.
0103: */
0104: private String name;
0105:
0106: private Status status;
0107:
0108: private CacheManagerPeerProvider cacheManagerPeerProvider;
0109: private CacheManagerPeerListener cacheManagerPeerListener;
0110: private CacheManagerEventListenerRegistry cacheManagerEventListenerRegistry = new CacheManagerEventListenerRegistry();
0111:
0112: /**
0113: * The shutdown hook thread for CacheManager. This ensures that the CacheManager and Caches are left in a
0114: * consistent state on a CTRL-C or kill.
0115: * <p/>
0116: * This thread must be unregistered as a shutdown hook, when the CacheManager is disposed.
0117: * Otherwise the CacheManager is not GC-able.
0118: * <p/>
0119: * Of course kill -9 or abrupt termination will not run the shutdown hook. In this case, various
0120: * sanity checks are made at start up.
0121: */
0122: private Thread shutdownHook;
0123:
0124: /**
0125: * An constructor for CacheManager, which takes a configuration object, rather than one created by parsing
0126: * an ehcache.xml file. This constructor gives complete control over the creation of the CacheManager.
0127: * <p/>
0128: * Care should be taken to ensure that, if multiple CacheManages are created, they do now overwrite each others
0129: * disk store files, as would happend if two were created which used the same diskStore path.
0130: * <p/>
0131: * This method does not act as a singleton. Callers must maintain their own reference to it.
0132: * <p/>
0133: * Note that if one of the {@link #create()} methods are called, a new singleton instance will be created,
0134: * separate from any instances created in this method.
0135: *
0136: * @param configuration
0137: * @throws CacheException
0138: */
0139: public CacheManager(Configuration configuration)
0140: throws CacheException {
0141: status = Status.STATUS_UNINITIALISED;
0142: init(configuration, null, null, null);
0143: }
0144:
0145: /**
0146: * An ordinary constructor for CacheManager.
0147: * This method does not act as a singleton. Callers must maintain a reference to it.
0148: * Note that if one of the {@link #create()} methods are called, a new singleton will be created,
0149: * separate from any instances created in this method.
0150: *
0151: * @param configurationFileName an xml configuration file available through a file name. The configuration
0152: * {@link File} is created
0153: * using new <code>File(configurationFileName)</code>
0154: * @throws CacheException
0155: * @see #create(String)
0156: */
0157: public CacheManager(String configurationFileName)
0158: throws CacheException {
0159: status = Status.STATUS_UNINITIALISED;
0160: init(null, configurationFileName, null, null);
0161: }
0162:
0163: /**
0164: * An ordinary constructor for CacheManager.
0165: * This method does not act as a singleton. Callers must maintain a reference to it.
0166: * Note that if one of the {@link #create()} methods are called, a new singleton will be created,
0167: * separate from any instances created in this method.
0168: * <p/>
0169: * This method can be used to specify a configuration resource in the classpath other
0170: * than the default of \"/ehcache.xml\":
0171: * <pre>
0172: * URL url = this.getClass().getResource("/ehcache-2.xml");
0173: * </pre>
0174: * Note that {@link Class#getResource} will look for resources in the same package unless a leading "/"
0175: * is used, in which case it will look in the root of the classpath.
0176: * <p/>
0177: * You can also load a resource using other class loaders. e.g. {@link Thread#getContextClassLoader()}
0178: *
0179: * @param configurationURL an xml configuration available through a URL.
0180: * @throws CacheException
0181: * @see #create(java.net.URL)
0182: * @since 1.2
0183: */
0184: public CacheManager(URL configurationURL) throws CacheException {
0185: status = Status.STATUS_UNINITIALISED;
0186: init(null, null, configurationURL, null);
0187: }
0188:
0189: /**
0190: * An ordinary constructor for CacheManager.
0191: * This method does not act as a singleton. Callers must maintain a reference to it.
0192: * Note that if one of the {@link #create()} methods are called, a new singleton will be created,
0193: * separate from any instances created in this method.
0194: *
0195: * @param configurationInputStream an xml configuration file available through an inputstream
0196: * @throws CacheException
0197: * @see #create(java.io.InputStream)
0198: */
0199: public CacheManager(InputStream configurationInputStream)
0200: throws CacheException {
0201: status = Status.STATUS_UNINITIALISED;
0202: init(null, null, null, configurationInputStream);
0203: }
0204:
0205: /**
0206: * Constructor.
0207: *
0208: * @throws CacheException
0209: */
0210: public CacheManager() throws CacheException {
0211: //default config will be done
0212: status = Status.STATUS_UNINITIALISED;
0213: init(null, null, null, null);
0214: }
0215:
0216: private void init(Configuration configuration,
0217: String configurationFileName, URL configurationURL,
0218: InputStream configurationInputStream) {
0219: Configuration localConfiguration = configuration;
0220: if (configuration == null) {
0221: localConfiguration = parseConfiguration(
0222: configurationFileName, configurationURL,
0223: configurationInputStream);
0224: } else {
0225: localConfiguration
0226: .setSource("Programmatically configured.");
0227: }
0228:
0229: ConfigurationHelper configurationHelper = new ConfigurationHelper(
0230: this , localConfiguration);
0231: configure(configurationHelper);
0232: status = Status.STATUS_ALIVE;
0233: if (cacheManagerPeerProvider != null) {
0234: cacheManagerPeerProvider.init();
0235: }
0236: cacheManagerEventListenerRegistry.init();
0237: addShutdownHookIfRequired();
0238:
0239: //do this last
0240: addConfiguredCaches(configurationHelper);
0241:
0242: }
0243:
0244: /**
0245: * Loads configuration, either from the supplied {@link ConfigurationHelper} or by creating a new Configuration instance
0246: * from the configuration file referred to by file, inputstream or URL.
0247: * <p/>
0248: * Should only be called once.
0249: *
0250: * @param configurationFileName the file name to parse, or null
0251: * @param configurationURL the URL to pass, or null
0252: * @param configurationInputStream, the InputStream to parse, or null
0253: * @return the loaded configuration
0254: * @throws CacheException if the configuration cannot be parsed
0255: */
0256: private synchronized Configuration parseConfiguration(
0257: String configurationFileName, URL configurationURL,
0258: InputStream configurationInputStream) throws CacheException {
0259: reinitialisationCheck();
0260: Configuration configuration;
0261: String configurationSource;
0262: if (configurationFileName != null) {
0263: if (LOG.isDebugEnabled()) {
0264: LOG.debug("Configuring CacheManager from "
0265: + configurationFileName);
0266: }
0267: configuration = ConfigurationFactory
0268: .parseConfiguration(new File(configurationFileName));
0269: configurationSource = "file located at "
0270: + configurationFileName;
0271: } else if (configurationURL != null) {
0272: configuration = ConfigurationFactory
0273: .parseConfiguration(configurationURL);
0274: configurationSource = "URL of " + configurationURL;
0275: } else if (configurationInputStream != null) {
0276: configuration = ConfigurationFactory
0277: .parseConfiguration(configurationInputStream);
0278: configurationSource = "InputStream "
0279: + configurationInputStream;
0280: } else {
0281: if (LOG.isDebugEnabled()) {
0282: LOG.debug("Configuring ehcache from classpath.");
0283: }
0284: configuration = ConfigurationFactory.parseConfiguration();
0285: configurationSource = "classpath";
0286: }
0287: configuration.setSource(configurationSource);
0288: return configuration;
0289:
0290: }
0291:
0292: private void configure(ConfigurationHelper configurationHelper) {
0293:
0294: diskStorePath = configurationHelper.getDiskStorePath();
0295: detectAndFixDiskStorePathConflict(configurationHelper);
0296: cacheManagerEventListenerRegistry
0297: .registerListener(configurationHelper
0298: .createCacheManagerEventListener());
0299:
0300: cacheManagerPeerListener = configurationHelper
0301: .createCachePeerListener();
0302: cacheManagerEventListenerRegistry
0303: .registerListener(cacheManagerPeerListener);
0304: detectAndFixCacheManagerPeerListenerConflict(configurationHelper);
0305:
0306: ALL_CACHE_MANAGERS.add(this );
0307:
0308: cacheManagerPeerProvider = configurationHelper
0309: .createCachePeerProvider();
0310:
0311: defaultCache = configurationHelper.createDefaultCache();
0312:
0313: }
0314:
0315: private void detectAndFixDiskStorePathConflict(
0316: ConfigurationHelper configurationHelper) {
0317: if (diskStorePath == null) {
0318: if (LOG.isDebugEnabled()) {
0319: LOG
0320: .debug("No disk store path defined. Skipping disk store path conflict test.");
0321: }
0322: return;
0323: }
0324:
0325: for (int i = 0; i < ALL_CACHE_MANAGERS.size(); i++) {
0326: CacheManager cacheManager = (CacheManager) ALL_CACHE_MANAGERS
0327: .get(i);
0328: if (diskStorePath.equals(cacheManager.diskStorePath)) {
0329: String newDiskStorePath = diskStorePath
0330: + File.separator
0331: + DiskStore.generateUniqueDirectory();
0332: LOG
0333: .warn("Creating a new instance of CacheManager using the diskStorePath \""
0334: + diskStorePath
0335: + "\" which is already used"
0336: + " by an existing CacheManager.\nThe source of the configuration was "
0337: + configurationHelper
0338: .getConfigurationBean()
0339: .getConfigurationSource()
0340: + ".\n"
0341: + "The diskStore path for this CacheManager will be set to "
0342: + newDiskStorePath
0343: + ".\nTo avoid this"
0344: + " warning consider using the CacheManager factory methods to create a singleton CacheManager "
0345: + "or specifying a separate ehcache configuration (ehcache.xml) for each CacheManager instance.");
0346: diskStorePath = newDiskStorePath;
0347: break;
0348: }
0349:
0350: }
0351: }
0352:
0353: private void detectAndFixCacheManagerPeerListenerConflict(
0354: ConfigurationHelper configurationHelper) {
0355: if (cacheManagerPeerListener == null) {
0356: return;
0357: }
0358: String uniqueResourceIdentifier = cacheManagerPeerListener
0359: .getUniqueResourceIdentifier();
0360: for (int i = 0; i < ALL_CACHE_MANAGERS.size(); i++) {
0361: CacheManager cacheManager = (CacheManager) ALL_CACHE_MANAGERS
0362: .get(i);
0363: CacheManagerPeerListener otherCacheManagerPeerListener = cacheManager.cacheManagerPeerListener;
0364: if (otherCacheManagerPeerListener == null) {
0365: continue;
0366: }
0367: String otherUniqueResourceIdentifier = otherCacheManagerPeerListener
0368: .getUniqueResourceIdentifier();
0369: if (uniqueResourceIdentifier
0370: .equals(otherUniqueResourceIdentifier)) {
0371: LOG
0372: .warn("Creating a new instance of CacheManager with a CacheManagerPeerListener which "
0373: + "has a conflict on a resource that must be unique.\n"
0374: + "The resource is "
0375: + uniqueResourceIdentifier
0376: + ".\n"
0377: + "Attempting automatic resolution. The source of the configuration was "
0378: + configurationHelper
0379: .getConfigurationBean()
0380: .getConfigurationSource()
0381: + ".\n"
0382: + "To avoid this warning consider using the CacheManager factory methods to create a "
0383: + "singleton CacheManager "
0384: + "or specifying a separate ehcache configuration (ehcache.xml) for each CacheManager instance.");
0385: cacheManagerPeerListener
0386: .attemptResolutionOfUniqueResourceConflict();
0387: break;
0388: }
0389:
0390: }
0391: }
0392:
0393: private void addConfiguredCaches(
0394: ConfigurationHelper configurationHelper) {
0395: Set unitialisedCaches = configurationHelper.createCaches();
0396: for (Iterator iterator = unitialisedCaches.iterator(); iterator
0397: .hasNext();) {
0398: Ehcache unitialisedCache = (Ehcache) iterator.next();
0399: addCacheNoCheck(unitialisedCache);
0400: }
0401: }
0402:
0403: private void reinitialisationCheck() throws IllegalStateException {
0404: if (defaultCache != null || diskStorePath != null
0405: || ehcaches.size() != 0
0406: || status.equals(Status.STATUS_SHUTDOWN)) {
0407: throw new IllegalStateException(
0408: "Attempt to reinitialise the CacheManager");
0409: }
0410: }
0411:
0412: /**
0413: * A factory method to create a singleton CacheManager with default config, or return it if it exists.
0414: * <p/>
0415: * The configuration will be read, {@link Ehcache}s created and required stores initialized.
0416: * When the {@link CacheManager} is no longer required, call shutdown to free resources.
0417: *
0418: * @return the singleton CacheManager
0419: * @throws CacheException if the CacheManager cannot be created
0420: */
0421: public static CacheManager create() throws CacheException {
0422: synchronized (CacheManager.class) {
0423: if (singleton == null) {
0424: if (LOG.isDebugEnabled()) {
0425: LOG
0426: .debug("Creating new CacheManager with default config");
0427: }
0428: singleton = new CacheManager();
0429: } else {
0430: if (LOG.isDebugEnabled()) {
0431: LOG
0432: .debug("Attempting to create an existing singleton. Existing singleton returned.");
0433: }
0434: }
0435: return singleton;
0436: }
0437: }
0438:
0439: /**
0440: * A factory method to create a singleton CacheManager with default config, or return it if it exists.
0441: * <p/>
0442: * This has the same effect as {@link CacheManager#create}
0443: * <p/>
0444: * Same as {@link #create()}
0445: *
0446: * @return the singleton CacheManager
0447: * @throws CacheException if the CacheManager cannot be created
0448: */
0449: public static CacheManager getInstance() throws CacheException {
0450: return CacheManager.create();
0451: }
0452:
0453: /**
0454: * A factory method to create a singleton CacheManager with a specified configuration.
0455: *
0456: * @param configurationFileName an xml file compliant with the ehcache.xsd schema
0457: * <p/>
0458: * The configuration will be read, {@link Ehcache}s created and required stores initialized.
0459: * When the {@link CacheManager} is no longer required, call shutdown to free resources.
0460: */
0461: public static CacheManager create(String configurationFileName)
0462: throws CacheException {
0463: synchronized (CacheManager.class) {
0464: if (singleton == null) {
0465: if (LOG.isDebugEnabled()) {
0466: LOG
0467: .debug("Creating new CacheManager with config file: "
0468: + configurationFileName);
0469: }
0470: singleton = new CacheManager(configurationFileName);
0471: }
0472: return singleton;
0473: }
0474: }
0475:
0476: /**
0477: * A factory method to create a singleton CacheManager from an URL.
0478: * <p/>
0479: * This method can be used to specify a configuration resource in the classpath other
0480: * than the default of \"/ehcache.xml\":
0481: * This method can be used to specify a configuration resource in the classpath other
0482: * than the default of \"/ehcache.xml\":
0483: * <pre>
0484: * URL url = this.getClass().getResource("/ehcache-2.xml");
0485: * </pre>
0486: * Note that {@link Class#getResource} will look for resources in the same package unless a leading "/"
0487: * is used, in which case it will look in the root of the classpath.
0488: * <p/>
0489: * You can also load a resource using other class loaders. e.g. {@link Thread#getContextClassLoader()}
0490: *
0491: * @param configurationFileURL an URL to an xml file compliant with the ehcache.xsd schema
0492: * <p/>
0493: * The configuration will be read, {@link Ehcache}s created and required stores initialized.
0494: * When the {@link CacheManager} is no longer required, call shutdown to free resources.
0495: */
0496: public static CacheManager create(URL configurationFileURL)
0497: throws CacheException {
0498: synchronized (CacheManager.class) {
0499: if (singleton == null) {
0500: if (LOG.isDebugEnabled()) {
0501: LOG
0502: .debug("Creating new CacheManager with config URL: "
0503: + configurationFileURL);
0504: }
0505: singleton = new CacheManager(configurationFileURL);
0506:
0507: }
0508: return singleton;
0509: }
0510: }
0511:
0512: /**
0513: * A factory method to create a singleton CacheManager from a java.io.InputStream.
0514: * <p/>
0515: * This method makes it possible to use an inputstream for configuration.
0516: * Note: it is the clients responsibility to close the inputstream.
0517: * <p/>
0518: *
0519: * @param inputStream InputStream of xml compliant with the ehcache.xsd schema
0520: * <p/>
0521: * The configuration will be read, {@link Ehcache}s created and required stores initialized.
0522: * When the {@link CacheManager} is no longer required, call shutdown to free resources.
0523: */
0524: public static CacheManager create(InputStream inputStream)
0525: throws CacheException {
0526: synchronized (CacheManager.class) {
0527: if (singleton == null) {
0528: if (LOG.isDebugEnabled()) {
0529: LOG
0530: .debug("Creating new CacheManager with InputStream");
0531: }
0532: singleton = new CacheManager(inputStream);
0533: }
0534: return singleton;
0535: }
0536: }
0537:
0538: /**
0539: * Returns a concrete implementation of Cache, it it is available in the CacheManager.
0540: * Consider using getEhcache(String name) instead, which will return decorated caches that are registered.
0541: * <p/>
0542: * If a decorated ehcache is registered in CacheManager, an undecorated Cache with the same name will also exist.
0543: *
0544: * @return a Cache, if an object of that type exists by that name, else null
0545: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
0546: * @see #getEhcache(String)
0547: */
0548: public synchronized Cache getCache(String name)
0549: throws IllegalStateException, ClassCastException {
0550: checkStatus();
0551: return (Cache) caches.get(name);
0552: }
0553:
0554: /**
0555: * Gets an Ehcache
0556: * <p/>
0557: * @return a Cache, if an object of type Cache exists by that name, else null
0558: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
0559: */
0560: public synchronized Ehcache getEhcache(String name)
0561: throws IllegalStateException {
0562: checkStatus();
0563: return (Ehcache) ehcaches.get(name);
0564: }
0565:
0566: /**
0567: * Gets a draft JSR107 spec JCache.
0568: * <p/>
0569: * If a JCache does not exist for the name, but an ehcache does, a new JCache will be created dynamically and added
0570: * to the list of JCaches managed by this CacheManager.
0571: * Warning: JCache will change as the specification changes, so no guarantee of backward compatibility is made for this method.
0572: * @return a JSR 107 Cache, if an object of that type exists by that name, else null
0573: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
0574: */
0575: public synchronized JCache getJCache(String name)
0576: throws IllegalStateException {
0577: checkStatus();
0578: if (jCaches.get(name) != null) {
0579: return (JCache) jCaches.get(name);
0580: } else if (ehcaches.get(name) != null) {
0581: jCaches.put(name, new JCache((Ehcache) ehcaches.get(name),
0582: null));
0583: }
0584: return (JCache) jCaches.get(name);
0585: }
0586:
0587: /**
0588: * Some caches might be persistent, so we want to add a shutdown hook if that is the
0589: * case, so that the data and index can be written to disk.
0590: */
0591: private void addShutdownHookIfRequired() {
0592:
0593: String shutdownHookProperty = System
0594: .getProperty(ENABLE_SHUTDOWN_HOOK_PROPERTY);
0595: boolean enabled = PropertyUtil
0596: .parseBoolean(shutdownHookProperty);
0597: if (!enabled) {
0598: return;
0599: } else {
0600: LOG
0601: .info("The CacheManager shutdown hook is enabled because "
0602: + ENABLE_SHUTDOWN_HOOK_PROPERTY
0603: + " is set to true.");
0604:
0605: Thread localShutdownHook = new Thread() {
0606: public void run() {
0607: synchronized (this ) {
0608: if (status.equals(Status.STATUS_ALIVE)) {
0609: // clear shutdown hook reference to prevent
0610: // removeShutdownHook to remove it during shutdown
0611: shutdownHook = null;
0612:
0613: if (LOG.isInfoEnabled()) {
0614: LOG
0615: .info("VM shutting down with the CacheManager still active. Calling shutdown.");
0616: }
0617: shutdown();
0618: }
0619: }
0620: }
0621: };
0622:
0623: Runtime.getRuntime().addShutdownHook(localShutdownHook);
0624: shutdownHook = localShutdownHook;
0625: }
0626: }
0627:
0628: /**
0629: * Remove the shutdown hook to prevent leaving orphaned CacheManagers around. This
0630: * is called by {@link #shutdown()}} AFTER the status has been set to shutdown.
0631: */
0632: private void removeShutdownHook() {
0633: if (shutdownHook != null) {
0634: // remove shutdown hook
0635: try {
0636: Runtime.getRuntime().removeShutdownHook(shutdownHook);
0637: } catch (IllegalStateException e) {
0638: //This will be thrown if the VM is shutting down. In this case
0639: //we do not need to worry about leaving references to CacheManagers lying
0640: //around and the call is ok to fail.
0641: if (LOG.isDebugEnabled()) {
0642: LOG
0643: .debug(
0644: "IllegalStateException due to attempt to remove a shutdown"
0645: + "hook while the VM is actually shutting down.",
0646: e);
0647: }
0648: }
0649: shutdownHook = null;
0650: }
0651: }
0652:
0653: /**
0654: * Adds a {@link Ehcache} based on the defaultCache with the given name.
0655: * <p/>
0656: * Memory and Disk stores will be configured for it and it will be added
0657: * to the map of caches.
0658: * <p/>
0659: * Also notifies the CacheManagerEventListener after the cache was initialised and added.
0660: * <p/>
0661: * It will be created with the defaultCache attributes specified in ehcache.xml
0662: *
0663: * @param cacheName the name for the cache
0664: * @throws ObjectExistsException if the cache already exists
0665: * @throws CacheException if there was an error creating the cache.
0666: */
0667: public synchronized void addCache(String cacheName)
0668: throws IllegalStateException, ObjectExistsException,
0669: CacheException {
0670: checkStatus();
0671:
0672: //NPE guard
0673: if (cacheName == null || cacheName.length() == 0) {
0674: return;
0675: }
0676:
0677: if (ehcaches.get(cacheName) != null) {
0678: throw new ObjectExistsException("Cache " + cacheName
0679: + " already exists");
0680: }
0681: Ehcache cache = null;
0682: try {
0683: cache = (Ehcache) defaultCache.clone();
0684: } catch (CloneNotSupportedException e) {
0685: throw new CacheException(
0686: "Failure adding cache. Initial cause was "
0687: + e.getMessage(), e);
0688: }
0689: if (cache != null) {
0690: cache.setName(cacheName);
0691: }
0692: addCache(cache);
0693: }
0694:
0695: /**
0696: * Adds a {@link Cache} to the CacheManager.
0697: * <p/>
0698: * Memory and Disk stores will be configured for it and it will be added to the map of caches.
0699: * Also notifies the CacheManagerEventListener after the cache was initialised and added.
0700: *
0701: * @param cache
0702: * @throws IllegalStateException if the cache is not {@link Status#STATUS_UNINITIALISED} before this method is called.
0703: * @throws ObjectExistsException if the cache already exists in the CacheManager
0704: * @throws CacheException if there was an error adding the cache to the CacheManager
0705: */
0706: public synchronized void addCache(Cache cache)
0707: throws IllegalStateException, ObjectExistsException,
0708: CacheException {
0709: checkStatus();
0710: if (cache == null) {
0711: return;
0712: }
0713: addCache((Ehcache) cache);
0714: caches.put(cache.getName(), cache);
0715: }
0716:
0717: /**
0718: * Adds a {@link Cache} to the CacheManager.
0719: * <p/>
0720: * Memory and Disk stores will be configured for it and it will be added to the map of caches.
0721: * Also notifies the CacheManagerEventListener after the cache was initialised and added.
0722: *
0723: * @param jCache
0724: * @throws IllegalStateException if the cache is not {@link Status#STATUS_UNINITIALISED} before this method is called.
0725: * @throws ObjectExistsException if the cache already exists in the CacheManager
0726: * @throws CacheException if there was an error adding the cache to the CacheManager
0727: */
0728: public synchronized void addCache(JCache jCache)
0729: throws IllegalStateException, ObjectExistsException,
0730: CacheException {
0731: checkStatus();
0732: if (jCache == null) {
0733: return;
0734: }
0735: Ehcache backingCache = jCache.getBackingCache();
0736: addCache(backingCache);
0737: jCaches.put(backingCache.getName(), jCache);
0738: }
0739:
0740: /**
0741: * Adds an {@link Ehcache} to the CacheManager.
0742: * <p/>
0743: * Memory and Disk stores will be configured for it and it will be added to the map of caches.
0744: * Also notifies the CacheManagerEventListener after the cache was initialised and added.
0745: *
0746: * @param cache
0747: * @throws IllegalStateException if the cache is not {@link Status#STATUS_UNINITIALISED} before this method is called.
0748: * @throws ObjectExistsException if the cache already exists in the CacheManager
0749: * @throws CacheException if there was an error adding the cache to the CacheManager
0750: */
0751: public synchronized void addCache(Ehcache cache)
0752: throws IllegalStateException, ObjectExistsException,
0753: CacheException {
0754: checkStatus();
0755: if (cache == null) {
0756: return;
0757: }
0758: addCacheNoCheck(cache);
0759: }
0760:
0761: private synchronized void addCacheNoCheck(Ehcache cache)
0762: throws IllegalStateException, ObjectExistsException,
0763: CacheException {
0764: if (ehcaches.get(cache.getName()) != null) {
0765: throw new ObjectExistsException("Cache " + cache.getName()
0766: + " already exists");
0767: }
0768: cache.setCacheManager(this );
0769: cache.setDiskStorePath(diskStorePath);
0770: cache.initialise();
0771: try {
0772: cache.bootstrap();
0773: } catch (CacheException e) {
0774: LOG
0775: .warn(
0776: "Cache "
0777: + cache.getName()
0778: + "requested bootstrap but a CacheException occured. "
0779: + e.getMessage(), e);
0780: }
0781: ehcaches.put(cache.getName(), cache);
0782: if (cache instanceof Cache) {
0783: caches.put(cache.getName(), cache);
0784: }
0785:
0786: //Don't notify initial config. The init method of each listener should take care of this.
0787: if (status.equals(Status.STATUS_ALIVE)) {
0788: cacheManagerEventListenerRegistry.notifyCacheAdded(cache
0789: .getName());
0790: }
0791: }
0792:
0793: /**
0794: * Checks whether a cache of type ehcache exists.
0795: * <p/>
0796: *
0797: * @param cacheName the cache name to check for
0798: * @return true if it exists
0799: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
0800: */
0801: public synchronized boolean cacheExists(String cacheName)
0802: throws IllegalStateException {
0803: checkStatus();
0804: return (ehcaches.get(cacheName) != null);
0805: }
0806:
0807: /**
0808: * Removes all caches using {@link #removeCache} for each cache.
0809: */
0810: public synchronized void removalAll() {
0811: String[] cacheNames = getCacheNames();
0812: for (int i = 0; i < cacheNames.length; i++) {
0813: String cacheName = cacheNames[i];
0814: removeCache(cacheName);
0815: }
0816: }
0817:
0818: /**
0819: * Remove a cache from the CacheManager. The cache is disposed of.
0820: *
0821: * @param cacheName the cache name
0822: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
0823: */
0824: public synchronized void removeCache(String cacheName)
0825: throws IllegalStateException {
0826: checkStatus();
0827:
0828: //NPE guard
0829: if (cacheName == null || cacheName.length() == 0) {
0830: return;
0831: }
0832: Ehcache cache = (Ehcache) ehcaches.remove(cacheName);
0833: if (cache != null
0834: && cache.getStatus().equals(Status.STATUS_ALIVE)) {
0835: cache.dispose();
0836: cacheManagerEventListenerRegistry.notifyCacheRemoved(cache
0837: .getName());
0838: }
0839: jCaches.remove(cacheName);
0840: caches.remove(cacheName);
0841: }
0842:
0843: /**
0844: * Shuts down the CacheManager.
0845: * <p/>
0846: * If the shutdown occurs on the singleton, then the singleton is removed, so that if a singleton access method
0847: * is called, a new singleton will be created.
0848: * <p/>
0849: * By default there is no shutdown hook (ehcache-1.3-beta2 and higher).
0850: * <p/>
0851: * Set the system property net.sf.ehcache.enableShutdownHook=true to turn it on.
0852: */
0853: public void shutdown() {
0854: synchronized (CacheManager.class) {
0855: if (status.equals(Status.STATUS_SHUTDOWN)) {
0856: if (LOG.isDebugEnabled()) {
0857: LOG.debug("CacheManager already shutdown");
0858: }
0859: return;
0860: }
0861: if (cacheManagerPeerProvider != null) {
0862: cacheManagerPeerProvider.dispose();
0863: }
0864:
0865: cacheManagerEventListenerRegistry.dispose();
0866:
0867: synchronized (CacheManager.class) {
0868: ALL_CACHE_MANAGERS.remove(this );
0869:
0870: Collection cacheSet = ehcaches.values();
0871: for (Iterator iterator = cacheSet.iterator(); iterator
0872: .hasNext();) {
0873: Ehcache cache = (Ehcache) iterator.next();
0874: if (cache != null) {
0875: cache.dispose();
0876: }
0877: }
0878: defaultCache.dispose();
0879: status = Status.STATUS_SHUTDOWN;
0880:
0881: //only delete singleton if the singleton is shutting down.
0882: if (this == singleton) {
0883: singleton = null;
0884: }
0885: removeShutdownHook();
0886: }
0887: }
0888: }
0889:
0890: /**
0891: * Returns a list of the current cache names.
0892: *
0893: * @return an array of {@link String}s
0894: * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
0895: */
0896: public synchronized String[] getCacheNames()
0897: throws IllegalStateException {
0898: checkStatus();
0899: String[] list = new String[ehcaches.size()];
0900: return (String[]) ehcaches.keySet().toArray(list);
0901: }
0902:
0903: private void checkStatus() {
0904: if (!(status.equals(Status.STATUS_ALIVE))) {
0905: throw new IllegalStateException(
0906: "The CacheManager is not alive.");
0907: }
0908: }
0909:
0910: /**
0911: * Gets the status attribute of the Ehcache
0912: *
0913: * @return The status value from the Status enum class
0914: */
0915: public Status getStatus() {
0916: return status;
0917: }
0918:
0919: /**
0920: * Clears the contents of all caches in the CacheManager, but without
0921: * removing any caches.
0922: * <p/>
0923: * This method is not synchronized. It only guarantees to clear those elements in a cache
0924: * at the time that the {@link Ehcache#removeAll()} mehod on each cache is called.
0925: */
0926: public void clearAll() throws CacheException {
0927: String[] cacheNames = getCacheNames();
0928: if (LOG.isDebugEnabled()) {
0929: LOG.debug("Clearing all caches");
0930: }
0931: for (int i = 0; i < cacheNames.length; i++) {
0932: String cacheName = cacheNames[i];
0933: Ehcache cache = getEhcache(cacheName);
0934: cache.removeAll();
0935: }
0936: }
0937:
0938: /**
0939: * Gets the <code>CacheManagerPeerProvider</code>
0940: * For distributed caches, the peer provider finds other cache managers and their caches in the same cluster
0941: *
0942: * @return the provider, or null if one does not exist
0943: */
0944: public CacheManagerPeerProvider getCachePeerProvider() {
0945: return cacheManagerPeerProvider;
0946: }
0947:
0948: /**
0949: * When CacheManage is configured as part of a cluster, a CacheManagerPeerListener will
0950: * be registered in it. Use this to access the individual cache listeners
0951: *
0952: * @return the listener, or null if one does not exist
0953: */
0954: public CacheManagerPeerListener getCachePeerListener() {
0955: return cacheManagerPeerListener;
0956: }
0957:
0958: /**
0959: * Returns the composite listener. A notification sent to this listener will notify all registered
0960: * listeners.
0961: *
0962: * @return null if none
0963: * @see "getCacheManagerEventListenerRegistry"
0964: */
0965: public CacheManagerEventListener getCacheManagerEventListener() {
0966: return cacheManagerEventListenerRegistry;
0967: }
0968:
0969: /**
0970: * Same as getCacheManagerEventListenerRegistry().registerListener(cacheManagerEventListener);
0971: * Left for backward compatiblity
0972: *
0973: * @param cacheManagerEventListener the listener to set.
0974: * @see "getCacheManagerEventListenerRegistry"
0975: * @deprecated Use getCacheManagerEventListenerRegistry instead
0976: */
0977: public void setCacheManagerEventListener(
0978: CacheManagerEventListener cacheManagerEventListener) {
0979: getCacheManagerEventListenerRegistry().registerListener(
0980: cacheManagerEventListener);
0981: }
0982:
0983: /**
0984: * Gets the CacheManagerEventListenerRegistry. Add and remove listeners here.
0985: */
0986: public CacheManagerEventListenerRegistry getCacheManagerEventListenerRegistry() {
0987: return cacheManagerEventListenerRegistry;
0988: }
0989:
0990: /**
0991: * Gets the CacheManagerPeerProvider, which can be useful for programmatically adding peers. Adding peers
0992: * will only be useful if the peer providers are manually provided rather than automatically discovered, otherwise
0993: * they will go stale.
0994: *
0995: * @return the CacheManagerPeerProvider, or null if there is not one.
0996: */
0997: public CacheManagerPeerProvider getCacheManagerPeerProvider() {
0998: return cacheManagerPeerProvider;
0999: }
1000:
1001: /**
1002: * Replaces in the map of Caches managed by this CacheManager an Ehcache with a decorated version of the same
1003: * Ehcache. CacheManager can operate fully with a decorated Ehcache.
1004: * <p/>
1005: * Ehcache Decorators can be used to obtain different behaviour from an Ehcache in a very flexible way. Some examples in
1006: * ehcache are:
1007: * <ol>
1008: * <li>{@link net.sf.ehcache.constructs.blocking.BlockingCache} - A cache that blocks other threads from getting a null element until the first thread
1009: * has placed a value in it.
1010: * <li>{@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache} - A BlockingCache that has the additional
1011: * property of knowing how to load its own entries.
1012: * </ol>
1013: * Many other kinds are possible.
1014: * <p/>
1015: * It is generally required that a decorated cache, once constructed, is made available to other execution threads.
1016: * The simplest way of doing this is to substitute the original cache for the decorated one here.
1017: * <p/>
1018: * Note that any overwritten Ehcache methods will take on new behaviours without casting. Casting is only required
1019: * for new methods that the decorator introduces.
1020: * For more information see the well known Gang of Four Decorator pattern.
1021: *
1022: * @param ehcache
1023: * @param decoratedCache An implementation of Ehcache that wraps the original cache.
1024: * @throws CacheException if the two caches do not equal each other.
1025: */
1026: public synchronized void replaceCacheWithDecoratedCache(
1027: Ehcache ehcache, Ehcache decoratedCache)
1028: throws CacheException {
1029: if (!ehcache.equals(decoratedCache)) {
1030: throw new CacheException("Cannot replace "
1031: + decoratedCache.getName()
1032: + " It does not equal the incumbent cache.");
1033: } else {
1034: String cacheName = ehcache.getName();
1035: ehcaches.remove(cacheName);
1036: caches.remove(cacheName);
1037: ehcaches.put(decoratedCache.getName(), decoratedCache);
1038: if (decoratedCache instanceof Cache) {
1039: caches.put(decoratedCache.getName(), decoratedCache);
1040: }
1041: }
1042:
1043: }
1044:
1045: /**
1046: * Replaces in the map of Caches managed by this CacheManager an Ehcache with a JCache decorated version of the <i>same</i> (see Ehcache equals method)
1047: * Ehcache, in a single synchronized method.
1048: * <p/>
1049: * Warning: JCache will change as the specification changes, so no guarantee of backward compatibility is made for this method.
1050: * @param ehcache
1051: * @param jCache A JCache that wraps the original cache.
1052: * @throws CacheException
1053: */
1054: public synchronized void replaceEhcacheWithJCache(Ehcache ehcache,
1055: JCache jCache) throws CacheException {
1056: if (!ehcache.getName().equals(
1057: jCache.getBackingCache().getName())) {
1058: throw new CacheException(
1059: "Cannot replace ehcache with a JCache where the backing cache has a different name");
1060: }
1061: Ehcache backingCache = jCache.getBackingCache();
1062: if (!ehcache.equals(backingCache)) {
1063: throw new CacheException("Cannot replace "
1064: + backingCache.getName()
1065: + " It does not equal the incumbent cache.");
1066: } else {
1067: jCaches.put(backingCache.getName(), jCache);
1068: }
1069: }
1070:
1071: /**
1072: * Gets the name of the CacheManager. This is useful for distinguishing multiple CacheManagers
1073: *
1074: * @return the name, or the output of toString() if it is not set.
1075: * @see #toString() which uses either the name or Object.toString()
1076: */
1077: public String getName() {
1078: if (name != null) {
1079: return name;
1080: } else {
1081: return super .toString();
1082: }
1083: }
1084:
1085: /**
1086: * Sets the name of the CacheManager. This is useful for distinguishing multiple CacheManagers
1087: * in a monitoring situation.
1088: *
1089: * @param name a name with characters legal in a JMX ObjectName
1090: */
1091: public void setName(String name) {
1092: this .name = name;
1093: }
1094:
1095: /**
1096: * @return either the name of this CacheManager, or if unset, Object.toString()
1097: */
1098: public String toString() {
1099: return getName();
1100: }
1101: }
|