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