001: /*
002: * Copyright 2002-2006 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: * http://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:
017: package org.springframework.cache.ehcache;
018:
019: import java.io.IOException;
020:
021: import net.sf.ehcache.Cache;
022: import net.sf.ehcache.CacheException;
023: import net.sf.ehcache.CacheManager;
024: import net.sf.ehcache.Ehcache;
025: import net.sf.ehcache.constructs.blocking.BlockingCache;
026: import net.sf.ehcache.constructs.blocking.CacheEntryFactory;
027: import net.sf.ehcache.constructs.blocking.SelfPopulatingCache;
028: import net.sf.ehcache.constructs.blocking.UpdatingCacheEntryFactory;
029: import net.sf.ehcache.constructs.blocking.UpdatingSelfPopulatingCache;
030: import net.sf.ehcache.store.MemoryStoreEvictionPolicy;
031: import org.apache.commons.logging.Log;
032: import org.apache.commons.logging.LogFactory;
033:
034: import org.springframework.beans.factory.BeanNameAware;
035: import org.springframework.beans.factory.FactoryBean;
036: import org.springframework.beans.factory.InitializingBean;
037: import org.springframework.util.Assert;
038:
039: /**
040: * FactoryBean that creates a named EHCache {@link net.sf.ehcache.Cache} instance
041: * (or a decorator that implements the {@link net.sf.ehcache.Ehcache} interface),
042: * representing a cache region within an EHCache {@link net.sf.ehcache.CacheManager}.
043: *
044: * <p>If the specified named cache is not configured in the cache configuration descriptor,
045: * this FactoryBean will construct an instance of a Cache with the provided name and the
046: * specified cache properties and add it to the CacheManager for later retrieval. If some
047: * or all properties are not set at configuration time, this FactoryBean will use defaults.
048: *
049: * <p>Note: If the named Cache instance is found, the properties will be ignored and the
050: * Cache instance will be retrieved from the CacheManager.
051: *
052: * <p>Note: As of Spring 2.0, this FactoryBean is based on EHCache 1.2's API (in particular
053: * the Ehcache interface and the extended Cache constructor). It is not compatible with
054: * EHCache 1.1 anymore; please upgrade to EHCache 1.2.3 or higher.
055: *
056: * @author Dmitriy Kopylenko
057: * @author Juergen Hoeller
058: * @since 1.1.1
059: * @see #setCacheManager
060: * @see EhCacheManagerFactoryBean
061: * @see net.sf.ehcache.Cache
062: */
063: public class EhCacheFactoryBean implements FactoryBean, BeanNameAware,
064: InitializingBean {
065:
066: protected final Log logger = LogFactory.getLog(getClass());
067:
068: private CacheManager cacheManager;
069:
070: private String cacheName;
071:
072: private int maxElementsInMemory = 10000;
073:
074: private MemoryStoreEvictionPolicy memoryStoreEvictionPolicy = MemoryStoreEvictionPolicy.LRU;
075:
076: private boolean overflowToDisk = true;
077:
078: private String diskStorePath;
079:
080: private boolean eternal = false;
081:
082: private int timeToLive = 120;
083:
084: private int timeToIdle = 120;
085:
086: private boolean diskPersistent = false;
087:
088: private int diskExpiryThreadIntervalSeconds = 120;
089:
090: private boolean blocking = false;
091:
092: private CacheEntryFactory cacheEntryFactory;
093:
094: private String beanName;
095:
096: private Ehcache cache;
097:
098: /**
099: * Set a CacheManager from which to retrieve a named Cache instance.
100: * By default, <code>CacheManager.getInstance()</code> 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: this .cacheName = cacheName;
119: }
120:
121: /**
122: * Specify the maximum number of cached objects in memory.
123: * Default is 10000 elements.
124: */
125: public void setMaxElementsInMemory(int maxElementsInMemory) {
126: this .maxElementsInMemory = maxElementsInMemory;
127: }
128:
129: /**
130: * Set the memory style eviction policy for this cache.
131: * Supported values are "LRU", "LFU" and "FIFO", according to the
132: * constants defined in EHCache's MemoryStoreEvictionPolicy class.
133: * Default is "LRU".
134: */
135: public void setMemoryStoreEvictionPolicy(
136: MemoryStoreEvictionPolicy memoryStoreEvictionPolicy) {
137: Assert.notNull(memoryStoreEvictionPolicy,
138: "memoryStoreEvictionPolicy must not be null");
139: this .memoryStoreEvictionPolicy = memoryStoreEvictionPolicy;
140: }
141:
142: /**
143: * Set whether elements can overflow to disk when the in-memory cache
144: * has reached the maximum size limit. Default is "true".
145: */
146: public void setOverflowToDisk(boolean overflowToDisk) {
147: this .overflowToDisk = overflowToDisk;
148: }
149:
150: /**
151: * Set the location of temporary files for the disk store of this cache.
152: * Default is the CacheManager's disk store path.
153: */
154: public void setDiskStorePath(String diskStorePath) {
155: this .diskStorePath = diskStorePath;
156: }
157:
158: /**
159: * Set whether elements are considered as eternal. If "true", timeouts
160: * are ignored and the element is never expired. Default is "false".
161: */
162: public void setEternal(boolean eternal) {
163: this .eternal = eternal;
164: }
165:
166: /**
167: * Set t he time in seconds to live for an element before it expires,
168: * i.e. the maximum time between creation time and when an element expires.
169: * It is only used if the element is not eternal. Default is 120 seconds.
170: */
171: public void setTimeToLive(int timeToLive) {
172: this .timeToLive = timeToLive;
173: }
174:
175: /**
176: * Set the time in seconds to idle for an element before it expires, that is,
177: * the maximum amount of time between accesses before an element expires.
178: * This is only used if the element is not eternal. Default is 120 seconds.
179: */
180: public void setTimeToIdle(int timeToIdle) {
181: this .timeToIdle = timeToIdle;
182: }
183:
184: /**
185: * Set whether the disk store persists between restarts of the Virtual Machine.
186: * The default is "false".
187: */
188: public void setDiskPersistent(boolean diskPersistent) {
189: this .diskPersistent = diskPersistent;
190: }
191:
192: /**
193: * Set the number of seconds between runs of the disk expiry thread.
194: * The default is 120 seconds.
195: */
196: public void setDiskExpiryThreadIntervalSeconds(
197: int diskExpiryThreadIntervalSeconds) {
198: this .diskExpiryThreadIntervalSeconds = diskExpiryThreadIntervalSeconds;
199: }
200:
201: /**
202: * Set whether to use a blocking cache that lets read attempts block
203: * until the requested element is created.
204: * <p>If you intend to build a self-populating blocking cache,
205: * consider specifying a {@link #setCacheEntryFactory CacheEntryFactory}.
206: * @see net.sf.ehcache.constructs.blocking.BlockingCache
207: * @see #setCacheEntryFactory
208: */
209: public void setBlocking(boolean blocking) {
210: this .blocking = blocking;
211: }
212:
213: /**
214: * Set an EHCache {@link net.sf.ehcache.constructs.blocking.CacheEntryFactory}
215: * to use for a self-populating cache. If such a factory is specified,
216: * the cache will be decorated with EHCache's
217: * {@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache}.
218: * <p>The specified factory can be of type
219: * {@link net.sf.ehcache.constructs.blocking.UpdatingCacheEntryFactory},
220: * which will lead to the use of an
221: * {@link net.sf.ehcache.constructs.blocking.UpdatingSelfPopulatingCache}.
222: * <p>Note: Any such self-populating cache is automatically a blocking cache.
223: * @see net.sf.ehcache.constructs.blocking.SelfPopulatingCache
224: * @see net.sf.ehcache.constructs.blocking.UpdatingSelfPopulatingCache
225: * @see net.sf.ehcache.constructs.blocking.UpdatingCacheEntryFactory
226: */
227: public void setCacheEntryFactory(CacheEntryFactory cacheEntryFactory) {
228: this .cacheEntryFactory = cacheEntryFactory;
229: }
230:
231: public void setBeanName(String name) {
232: this .beanName = name;
233: }
234:
235: public void afterPropertiesSet() throws CacheException, IOException {
236: // If no CacheManager given, fetch the default.
237: if (this .cacheManager == null) {
238: if (logger.isDebugEnabled()) {
239: logger
240: .debug("Using default EHCache CacheManager for cache region '"
241: + this .cacheName + "'");
242: }
243: this .cacheManager = CacheManager.getInstance();
244: }
245:
246: // If no cache name given, use bean name as cache name.
247: if (this .cacheName == null) {
248: this .cacheName = this .beanName;
249: }
250:
251: // Fetch cache region: If none with the given name exists,
252: // create one on the fly.
253: if (this .cacheManager.cacheExists(this .cacheName)) {
254: if (logger.isDebugEnabled()) {
255: logger.debug("Using existing EHCache cache region '"
256: + this .cacheName + "'");
257: }
258: this .cache = this .cacheManager.getEhcache(this .cacheName);
259: } else {
260: if (logger.isDebugEnabled()) {
261: logger.debug("Creating new EHCache cache region '"
262: + this .cacheName + "'");
263: }
264: Cache rawCache = createCache();
265: this .cacheManager.addCache(rawCache);
266: Ehcache decoratedCache = decorateCache(rawCache);
267: this .cacheManager.replaceCacheWithDecoratedCache(rawCache,
268: decoratedCache);
269: this .cache = decoratedCache;
270: }
271: }
272:
273: /**
274: * Create a raw Cache object based on the configuration of this FactoryBean.
275: */
276: private Cache createCache() {
277: return new Cache(this .cacheName, this .maxElementsInMemory,
278: this .memoryStoreEvictionPolicy, this .overflowToDisk,
279: this .diskStorePath, this .eternal, this .timeToLive,
280: this .timeToIdle, this .diskPersistent,
281: this .diskExpiryThreadIntervalSeconds, null);
282: }
283:
284: /**
285: * Decorate the given Cache, if necessary.
286: * <p>The default implementation simply returns the given cache object as-is.
287: * @param cache the raw Cache object, based on the configuration of this FactoryBean
288: * @return the (potentially decorated) cache object to be registered with the CacheManager
289: */
290: protected Ehcache decorateCache(Cache cache) {
291: if (this .cacheEntryFactory != null) {
292: if (this .cacheEntryFactory instanceof UpdatingCacheEntryFactory) {
293: return new UpdatingSelfPopulatingCache(
294: cache,
295: (UpdatingCacheEntryFactory) this .cacheEntryFactory);
296: } else {
297: return new SelfPopulatingCache(cache,
298: this .cacheEntryFactory);
299: }
300: }
301: if (this .blocking) {
302: return new BlockingCache(cache);
303: }
304: return cache;
305: }
306:
307: public Object getObject() {
308: return this .cache;
309: }
310:
311: public Class getObjectType() {
312: return (this .cache != null ? this .cache.getClass()
313: : Ehcache.class);
314: }
315:
316: public boolean isSingleton() {
317: return true;
318: }
319:
320: }
|