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