001/* 002 * Copyright 2002-2018 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 java.util.Set; 020 021import net.sf.ehcache.Cache; 022import net.sf.ehcache.CacheException; 023import net.sf.ehcache.CacheManager; 024import net.sf.ehcache.Ehcache; 025import net.sf.ehcache.bootstrap.BootstrapCacheLoader; 026import net.sf.ehcache.config.CacheConfiguration; 027import net.sf.ehcache.constructs.blocking.BlockingCache; 028import net.sf.ehcache.constructs.blocking.CacheEntryFactory; 029import net.sf.ehcache.constructs.blocking.SelfPopulatingCache; 030import net.sf.ehcache.constructs.blocking.UpdatingCacheEntryFactory; 031import net.sf.ehcache.constructs.blocking.UpdatingSelfPopulatingCache; 032import net.sf.ehcache.event.CacheEventListener; 033import org.apache.commons.logging.Log; 034import org.apache.commons.logging.LogFactory; 035 036import org.springframework.beans.factory.BeanNameAware; 037import org.springframework.beans.factory.FactoryBean; 038import org.springframework.beans.factory.InitializingBean; 039import org.springframework.lang.Nullable; 040 041/** 042 * {@link FactoryBean} that creates a named EhCache {@link net.sf.ehcache.Cache} instance 043 * (or a decorator that implements the {@link net.sf.ehcache.Ehcache} interface), 044 * representing a cache region within an EhCache {@link net.sf.ehcache.CacheManager}. 045 * 046 * <p>If the specified named cache is not configured in the cache configuration descriptor, 047 * this FactoryBean will construct an instance of a Cache with the provided name and the 048 * specified cache properties and add it to the CacheManager for later retrieval. If some 049 * or all properties are not set at configuration time, this FactoryBean will use defaults. 050 * 051 * <p>Note: If the named Cache instance is found, the properties will be ignored and the 052 * Cache instance will be retrieved from the CacheManager. 053 * 054 * <p>Note: As of Spring 5.0, Spring's EhCache support requires EhCache 2.10 or higher. 055 * 056 * @author Juergen Hoeller 057 * @author Dmitriy Kopylenko 058 * @since 1.1.1 059 * @see #setCacheManager 060 * @see EhCacheManagerFactoryBean 061 * @see net.sf.ehcache.Cache 062 */ 063public class EhCacheFactoryBean extends CacheConfiguration implements FactoryBean<Ehcache>, BeanNameAware, InitializingBean { 064 065 protected final Log logger = LogFactory.getLog(getClass()); 066 067 @Nullable 068 private CacheManager cacheManager; 069 070 private boolean blocking = false; 071 072 @Nullable 073 private CacheEntryFactory cacheEntryFactory; 074 075 @Nullable 076 private BootstrapCacheLoader bootstrapCacheLoader; 077 078 @Nullable 079 private Set<CacheEventListener> cacheEventListeners; 080 081 private boolean disabled = false; 082 083 @Nullable 084 private String beanName; 085 086 @Nullable 087 private Ehcache cache; 088 089 090 public EhCacheFactoryBean() { 091 setMaxEntriesLocalHeap(10000); 092 setMaxEntriesLocalDisk(10000000); 093 setTimeToLiveSeconds(120); 094 setTimeToIdleSeconds(120); 095 } 096 097 098 /** 099 * Set a CacheManager from which to retrieve a named Cache instance. 100 * By default, {@code CacheManager.getInstance()} will be called. 101 * <p>Note that in particular for persistent caches, it is advisable to 102 * properly handle the shutdown of the CacheManager: Set up a separate 103 * EhCacheManagerFactoryBean and pass a reference to this bean property. 104 * <p>A separate EhCacheManagerFactoryBean is also necessary for loading 105 * EhCache configuration from a non-default config location. 106 * @see EhCacheManagerFactoryBean 107 * @see net.sf.ehcache.CacheManager#getInstance 108 */ 109 public void setCacheManager(CacheManager cacheManager) { 110 this.cacheManager = cacheManager; 111 } 112 113 /** 114 * Set a name for which to retrieve or create a cache instance. 115 * Default is the bean name of this EhCacheFactoryBean. 116 */ 117 public void setCacheName(String cacheName) { 118 setName(cacheName); 119 } 120 121 /** 122 * Set the time to live. 123 * @see #setTimeToLiveSeconds(long) 124 */ 125 public void setTimeToLive(int timeToLive) { 126 setTimeToLiveSeconds(timeToLive); 127 } 128 129 /** 130 * Set the time to idle. 131 * @see #setTimeToIdleSeconds(long) 132 */ 133 public void setTimeToIdle(int timeToIdle) { 134 setTimeToIdleSeconds(timeToIdle); 135 } 136 137 /** 138 * Set the disk spool buffer size (in MB). 139 * @see #setDiskSpoolBufferSizeMB(int) 140 */ 141 public void setDiskSpoolBufferSize(int diskSpoolBufferSize) { 142 setDiskSpoolBufferSizeMB(diskSpoolBufferSize); 143 } 144 145 /** 146 * Set whether to use a blocking cache that lets read attempts block 147 * until the requested element is created. 148 * <p>If you intend to build a self-populating blocking cache, 149 * consider specifying a {@link #setCacheEntryFactory CacheEntryFactory}. 150 * @see net.sf.ehcache.constructs.blocking.BlockingCache 151 * @see #setCacheEntryFactory 152 */ 153 public void setBlocking(boolean blocking) { 154 this.blocking = blocking; 155 } 156 157 /** 158 * Set an EhCache {@link net.sf.ehcache.constructs.blocking.CacheEntryFactory} 159 * to use for a self-populating cache. If such a factory is specified, 160 * the cache will be decorated with EhCache's 161 * {@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache}. 162 * <p>The specified factory can be of type 163 * {@link net.sf.ehcache.constructs.blocking.UpdatingCacheEntryFactory}, 164 * which will lead to the use of an 165 * {@link net.sf.ehcache.constructs.blocking.UpdatingSelfPopulatingCache}. 166 * <p>Note: Any such self-populating cache is automatically a blocking cache. 167 * @see net.sf.ehcache.constructs.blocking.SelfPopulatingCache 168 * @see net.sf.ehcache.constructs.blocking.UpdatingSelfPopulatingCache 169 * @see net.sf.ehcache.constructs.blocking.UpdatingCacheEntryFactory 170 */ 171 public void setCacheEntryFactory(CacheEntryFactory cacheEntryFactory) { 172 this.cacheEntryFactory = cacheEntryFactory; 173 } 174 175 /** 176 * Set an EhCache {@link net.sf.ehcache.bootstrap.BootstrapCacheLoader} 177 * for this cache, if any. 178 */ 179 public void setBootstrapCacheLoader(BootstrapCacheLoader bootstrapCacheLoader) { 180 this.bootstrapCacheLoader = bootstrapCacheLoader; 181 } 182 183 /** 184 * Specify EhCache {@link net.sf.ehcache.event.CacheEventListener cache event listeners} 185 * to registered with this cache. 186 */ 187 public void setCacheEventListeners(Set<CacheEventListener> cacheEventListeners) { 188 this.cacheEventListeners = cacheEventListeners; 189 } 190 191 /** 192 * Set whether this cache should be marked as disabled. 193 * @see net.sf.ehcache.Cache#setDisabled 194 */ 195 public void setDisabled(boolean disabled) { 196 this.disabled = disabled; 197 } 198 199 @Override 200 public void setBeanName(String name) { 201 this.beanName = name; 202 } 203 204 205 @Override 206 public void afterPropertiesSet() throws CacheException { 207 // If no cache name given, use bean name as cache name. 208 String cacheName = getName(); 209 if (cacheName == null) { 210 cacheName = this.beanName; 211 if (cacheName != null) { 212 setName(cacheName); 213 } 214 } 215 216 // If no CacheManager given, fetch the default. 217 if (this.cacheManager == null) { 218 if (logger.isDebugEnabled()) { 219 logger.debug("Using default EhCache CacheManager for cache region '" + cacheName + "'"); 220 } 221 this.cacheManager = CacheManager.getInstance(); 222 } 223 224 synchronized (this.cacheManager) { 225 // Fetch cache region: If none with the given name exists, create one on the fly. 226 Ehcache rawCache; 227 boolean cacheExists = this.cacheManager.cacheExists(cacheName); 228 229 if (cacheExists) { 230 if (logger.isDebugEnabled()) { 231 logger.debug("Using existing EhCache cache region '" + cacheName + "'"); 232 } 233 rawCache = this.cacheManager.getEhcache(cacheName); 234 } 235 else { 236 if (logger.isDebugEnabled()) { 237 logger.debug("Creating new EhCache cache region '" + cacheName + "'"); 238 } 239 rawCache = createCache(); 240 rawCache.setBootstrapCacheLoader(this.bootstrapCacheLoader); 241 } 242 243 if (this.cacheEventListeners != null) { 244 for (CacheEventListener listener : this.cacheEventListeners) { 245 rawCache.getCacheEventNotificationService().registerListener(listener); 246 } 247 } 248 249 // Needs to happen after listener registration but before setStatisticsEnabled 250 if (!cacheExists) { 251 this.cacheManager.addCache(rawCache); 252 } 253 254 if (this.disabled) { 255 rawCache.setDisabled(true); 256 } 257 258 Ehcache decoratedCache = decorateCache(rawCache); 259 if (decoratedCache != rawCache) { 260 this.cacheManager.replaceCacheWithDecoratedCache(rawCache, decoratedCache); 261 } 262 this.cache = decoratedCache; 263 } 264 } 265 266 /** 267 * Create a raw Cache object based on the configuration of this FactoryBean. 268 */ 269 protected Cache createCache() { 270 return new Cache(this); 271 } 272 273 /** 274 * Decorate the given Cache, if necessary. 275 * @param cache the raw Cache object, based on the configuration of this FactoryBean 276 * @return the (potentially decorated) cache object to be registered with the CacheManager 277 */ 278 protected Ehcache decorateCache(Ehcache cache) { 279 if (this.cacheEntryFactory != null) { 280 if (this.cacheEntryFactory instanceof UpdatingCacheEntryFactory) { 281 return new UpdatingSelfPopulatingCache(cache, (UpdatingCacheEntryFactory) this.cacheEntryFactory); 282 } 283 else { 284 return new SelfPopulatingCache(cache, this.cacheEntryFactory); 285 } 286 } 287 if (this.blocking) { 288 return new BlockingCache(cache); 289 } 290 return cache; 291 } 292 293 294 @Override 295 @Nullable 296 public Ehcache getObject() { 297 return this.cache; 298 } 299 300 /** 301 * Predict the particular {@code Ehcache} implementation that will be returned from 302 * {@link #getObject()} based on logic in {@link #createCache()} and 303 * {@link #decorateCache(Ehcache)} as orchestrated by {@link #afterPropertiesSet()}. 304 */ 305 @Override 306 public Class<? extends Ehcache> getObjectType() { 307 if (this.cache != null) { 308 return this.cache.getClass(); 309 } 310 if (this.cacheEntryFactory != null) { 311 if (this.cacheEntryFactory instanceof UpdatingCacheEntryFactory) { 312 return UpdatingSelfPopulatingCache.class; 313 } 314 else { 315 return SelfPopulatingCache.class; 316 } 317 } 318 if (this.blocking) { 319 return BlockingCache.class; 320 } 321 return Cache.class; 322 } 323 324 @Override 325 public boolean isSingleton() { 326 return true; 327 } 328 329}