001/*
002 * Copyright 2002-2017 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.cache.ehcache;
018
019import net.sf.ehcache.CacheException;
020import net.sf.ehcache.CacheManager;
021import net.sf.ehcache.config.Configuration;
022import net.sf.ehcache.config.ConfigurationFactory;
023import org.apache.commons.logging.Log;
024import org.apache.commons.logging.LogFactory;
025
026import org.springframework.beans.factory.DisposableBean;
027import org.springframework.beans.factory.FactoryBean;
028import org.springframework.beans.factory.InitializingBean;
029import org.springframework.core.io.Resource;
030import org.springframework.lang.Nullable;
031
032/**
033 * {@link FactoryBean} that exposes an EhCache {@link net.sf.ehcache.CacheManager}
034 * instance (independent or shared), configured from a specified config location.
035 *
036 * <p>If no config location is specified, a CacheManager will be configured from
037 * "ehcache.xml" in the root of the class path (that is, default EhCache initialization
038 * - as defined in the EhCache docs - will apply).
039 *
040 * <p>Setting up a separate EhCacheManagerFactoryBean is also advisable when using
041 * EhCacheFactoryBean, as it provides a (by default) independent CacheManager instance
042 * and cares for proper shutdown of the CacheManager. EhCacheManagerFactoryBean is
043 * also necessary for loading EhCache configuration from a non-default config location.
044 *
045 * <p>Note: As of Spring 5.0, Spring's EhCache support requires EhCache 2.10 or higher.
046 *
047 * @author Juergen Hoeller
048 * @author Dmitriy Kopylenko
049 * @since 1.1.1
050 * @see #setConfigLocation
051 * @see #setShared
052 * @see EhCacheFactoryBean
053 * @see net.sf.ehcache.CacheManager
054 */
055public class EhCacheManagerFactoryBean implements FactoryBean<CacheManager>, InitializingBean, DisposableBean {
056
057        protected final Log logger = LogFactory.getLog(getClass());
058
059        @Nullable
060        private Resource configLocation;
061
062        @Nullable
063        private String cacheManagerName;
064
065        private boolean acceptExisting = false;
066
067        private boolean shared = false;
068
069        @Nullable
070        private CacheManager cacheManager;
071
072        private boolean locallyManaged = true;
073
074
075        /**
076         * Set the location of the EhCache config file. A typical value is "/WEB-INF/ehcache.xml".
077         * <p>Default is "ehcache.xml" in the root of the class path, or if not found,
078         * "ehcache-failsafe.xml" in the EhCache jar (default EhCache initialization).
079         * @see net.sf.ehcache.CacheManager#create(java.io.InputStream)
080         * @see net.sf.ehcache.CacheManager#CacheManager(java.io.InputStream)
081         */
082        public void setConfigLocation(Resource configLocation) {
083                this.configLocation = configLocation;
084        }
085
086        /**
087         * Set the name of the EhCache CacheManager (if a specific name is desired).
088         * @see net.sf.ehcache.config.Configuration#setName(String)
089         */
090        public void setCacheManagerName(String cacheManagerName) {
091                this.cacheManagerName = cacheManagerName;
092        }
093
094        /**
095         * Set whether an existing EhCache CacheManager of the same name will be accepted
096         * for this EhCacheManagerFactoryBean setup. Default is "false".
097         * <p>Typically used in combination with {@link #setCacheManagerName "cacheManagerName"}
098         * but will simply work with the default CacheManager name if none specified.
099         * All references to the same CacheManager name (or the same default) in the
100         * same ClassLoader space will share the specified CacheManager then.
101         * @see #setCacheManagerName
102         * #see #setShared
103         * @see net.sf.ehcache.CacheManager#getCacheManager(String)
104         * @see net.sf.ehcache.CacheManager#CacheManager()
105         */
106        public void setAcceptExisting(boolean acceptExisting) {
107                this.acceptExisting = acceptExisting;
108        }
109
110        /**
111         * Set whether the EhCache CacheManager should be shared (as a singleton at the
112         * ClassLoader level) or independent (typically local within the application).
113         * Default is "false", creating an independent local instance.
114         * <p><b>NOTE:</b> This feature allows for sharing this EhCacheManagerFactoryBean's
115         * CacheManager with any code calling <code>CacheManager.create()</code> in the same
116         * ClassLoader space, with no need to agree on a specific CacheManager name.
117         * However, it only supports a single EhCacheManagerFactoryBean involved which will
118         * control the lifecycle of the underlying CacheManager (in particular, its shutdown).
119         * <p>This flag overrides {@link #setAcceptExisting "acceptExisting"} if both are set,
120         * since it indicates the 'stronger' mode of sharing.
121         * @see #setCacheManagerName
122         * @see #setAcceptExisting
123         * @see net.sf.ehcache.CacheManager#create()
124         * @see net.sf.ehcache.CacheManager#CacheManager()
125         */
126        public void setShared(boolean shared) {
127                this.shared = shared;
128        }
129
130
131        @Override
132        public void afterPropertiesSet() throws CacheException {
133                if (logger.isInfoEnabled()) {
134                        logger.info("Initializing EhCache CacheManager" +
135                                        (this.cacheManagerName != null ? " '" + this.cacheManagerName + "'" : ""));
136                }
137
138                Configuration configuration = (this.configLocation != null ?
139                                EhCacheManagerUtils.parseConfiguration(this.configLocation) : ConfigurationFactory.parseConfiguration());
140                if (this.cacheManagerName != null) {
141                        configuration.setName(this.cacheManagerName);
142                }
143
144                if (this.shared) {
145                        // Old-school EhCache singleton sharing...
146                        // No way to find out whether we actually created a new CacheManager
147                        // or just received an existing singleton reference.
148                        this.cacheManager = CacheManager.create(configuration);
149                }
150                else if (this.acceptExisting) {
151                        // EhCache 2.5+: Reusing an existing CacheManager of the same name.
152                        // Basically the same code as in CacheManager.getInstance(String),
153                        // just storing whether we're dealing with an existing instance.
154                        synchronized (CacheManager.class) {
155                                this.cacheManager = CacheManager.getCacheManager(this.cacheManagerName);
156                                if (this.cacheManager == null) {
157                                        this.cacheManager = new CacheManager(configuration);
158                                }
159                                else {
160                                        this.locallyManaged = false;
161                                }
162                        }
163                }
164                else {
165                        // Throwing an exception if a CacheManager of the same name exists already...
166                        this.cacheManager = new CacheManager(configuration);
167                }
168        }
169
170
171        @Override
172        @Nullable
173        public CacheManager getObject() {
174                return this.cacheManager;
175        }
176
177        @Override
178        public Class<? extends CacheManager> getObjectType() {
179                return (this.cacheManager != null ? this.cacheManager.getClass() : CacheManager.class);
180        }
181
182        @Override
183        public boolean isSingleton() {
184                return true;
185        }
186
187
188        @Override
189        public void destroy() {
190                if (this.cacheManager != null && this.locallyManaged) {
191                        if (logger.isInfoEnabled()) {
192                                logger.info("Shutting down EhCache CacheManager" +
193                                                (this.cacheManagerName != null ? " '" + this.cacheManagerName + "'" : ""));
194                        }
195                        this.cacheManager.shutdown();
196                }
197        }
198
199}