001: /*
002: * Copyright 2002-2007 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.4 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 int maxElementsOnDisk = 10000000;
075:
076: private MemoryStoreEvictionPolicy memoryStoreEvictionPolicy = MemoryStoreEvictionPolicy.LRU;
077:
078: private boolean overflowToDisk = true;
079:
080: private String diskStorePath;
081:
082: private boolean eternal = false;
083:
084: private int timeToLive = 120;
085:
086: private int timeToIdle = 120;
087:
088: private boolean diskPersistent = false;
089:
090: private int diskExpiryThreadIntervalSeconds = 120;
091:
092: private boolean blocking = false;
093:
094: private CacheEntryFactory cacheEntryFactory;
095:
096: private String beanName;
097:
098: private Ehcache cache;
099:
100: /**
101: * Set a CacheManager from which to retrieve a named Cache instance.
102: * By default, <code>CacheManager.getInstance()</code> will be called.
103: * <p>Note that in particular for persistent caches, it is advisable to
104: * properly handle the shutdown of the CacheManager: Set up a separate
105: * EhCacheManagerFactoryBean and pass a reference to this bean property.
106: * <p>A separate EhCacheManagerFactoryBean is also necessary for loading
107: * EHCache configuration from a non-default config location.
108: * @see EhCacheManagerFactoryBean
109: * @see net.sf.ehcache.CacheManager#getInstance
110: */
111: public void setCacheManager(CacheManager cacheManager) {
112: this .cacheManager = cacheManager;
113: }
114:
115: /**
116: * Set a name for which to retrieve or create a cache instance.
117: * Default is the bean name of this EhCacheFactoryBean.
118: */
119: public void setCacheName(String cacheName) {
120: this .cacheName = cacheName;
121: }
122:
123: /**
124: * Specify the maximum number of cached objects in memory.
125: * Default is 10000 elements.
126: */
127: public void setMaxElementsInMemory(int maxElementsInMemory) {
128: this .maxElementsInMemory = maxElementsInMemory;
129: }
130:
131: /**
132: * Specify the maximum number of cached objects on disk.
133: * Default is 10000000 elements.
134: */
135: public void setMaxElementsOnDisk(int maxElementsOnDisk) {
136: this .maxElementsOnDisk = maxElementsOnDisk;
137: }
138:
139: /**
140: * Set the memory style eviction policy for this cache.
141: * Supported values are "LRU", "LFU" and "FIFO", according to the
142: * constants defined in EHCache's MemoryStoreEvictionPolicy class.
143: * Default is "LRU".
144: */
145: public void setMemoryStoreEvictionPolicy(
146: MemoryStoreEvictionPolicy memoryStoreEvictionPolicy) {
147: Assert.notNull(memoryStoreEvictionPolicy,
148: "memoryStoreEvictionPolicy must not be null");
149: this .memoryStoreEvictionPolicy = memoryStoreEvictionPolicy;
150: }
151:
152: /**
153: * Set whether elements can overflow to disk when the in-memory cache
154: * has reached the maximum size limit. Default is "true".
155: */
156: public void setOverflowToDisk(boolean overflowToDisk) {
157: this .overflowToDisk = overflowToDisk;
158: }
159:
160: /**
161: * Set the location of temporary files for the disk store of this cache.
162: * Default is the CacheManager's disk store path.
163: */
164: public void setDiskStorePath(String diskStorePath) {
165: this .diskStorePath = diskStorePath;
166: }
167:
168: /**
169: * Set whether elements are considered as eternal. If "true", timeouts
170: * are ignored and the element is never expired. Default is "false".
171: */
172: public void setEternal(boolean eternal) {
173: this .eternal = eternal;
174: }
175:
176: /**
177: * Set t he time in seconds to live for an element before it expires,
178: * i.e. the maximum time between creation time and when an element expires.
179: * It is only used if the element is not eternal. Default is 120 seconds.
180: */
181: public void setTimeToLive(int timeToLive) {
182: this .timeToLive = timeToLive;
183: }
184:
185: /**
186: * Set the time in seconds to idle for an element before it expires, that is,
187: * the maximum amount of time between accesses before an element expires.
188: * This is only used if the element is not eternal. Default is 120 seconds.
189: */
190: public void setTimeToIdle(int timeToIdle) {
191: this .timeToIdle = timeToIdle;
192: }
193:
194: /**
195: * Set whether the disk store persists between restarts of the Virtual Machine.
196: * The default is "false".
197: */
198: public void setDiskPersistent(boolean diskPersistent) {
199: this .diskPersistent = diskPersistent;
200: }
201:
202: /**
203: * Set the number of seconds between runs of the disk expiry thread.
204: * The default is 120 seconds.
205: */
206: public void setDiskExpiryThreadIntervalSeconds(
207: int diskExpiryThreadIntervalSeconds) {
208: this .diskExpiryThreadIntervalSeconds = diskExpiryThreadIntervalSeconds;
209: }
210:
211: /**
212: * Set whether to use a blocking cache that lets read attempts block
213: * until the requested element is created.
214: * <p>If you intend to build a self-populating blocking cache,
215: * consider specifying a {@link #setCacheEntryFactory CacheEntryFactory}.
216: * @see net.sf.ehcache.constructs.blocking.BlockingCache
217: * @see #setCacheEntryFactory
218: */
219: public void setBlocking(boolean blocking) {
220: this .blocking = blocking;
221: }
222:
223: /**
224: * Set an EHCache {@link net.sf.ehcache.constructs.blocking.CacheEntryFactory}
225: * to use for a self-populating cache. If such a factory is specified,
226: * the cache will be decorated with EHCache's
227: * {@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache}.
228: * <p>The specified factory can be of type
229: * {@link net.sf.ehcache.constructs.blocking.UpdatingCacheEntryFactory},
230: * which will lead to the use of an
231: * {@link net.sf.ehcache.constructs.blocking.UpdatingSelfPopulatingCache}.
232: * <p>Note: Any such self-populating cache is automatically a blocking cache.
233: * @see net.sf.ehcache.constructs.blocking.SelfPopulatingCache
234: * @see net.sf.ehcache.constructs.blocking.UpdatingSelfPopulatingCache
235: * @see net.sf.ehcache.constructs.blocking.UpdatingCacheEntryFactory
236: */
237: public void setCacheEntryFactory(CacheEntryFactory cacheEntryFactory) {
238: this .cacheEntryFactory = cacheEntryFactory;
239: }
240:
241: public void setBeanName(String name) {
242: this .beanName = name;
243: }
244:
245: public void afterPropertiesSet() throws CacheException, IOException {
246: // If no CacheManager given, fetch the default.
247: if (this .cacheManager == null) {
248: if (logger.isDebugEnabled()) {
249: logger
250: .debug("Using default EHCache CacheManager for cache region '"
251: + this .cacheName + "'");
252: }
253: this .cacheManager = CacheManager.getInstance();
254: }
255:
256: // If no cache name given, use bean name as cache name.
257: if (this .cacheName == null) {
258: this .cacheName = this .beanName;
259: }
260:
261: // Fetch cache region: If none with the given name exists,
262: // create one on the fly.
263: Cache rawCache = null;
264: if (this .cacheManager.cacheExists(this .cacheName)) {
265: if (logger.isDebugEnabled()) {
266: logger.debug("Using existing EHCache cache region '"
267: + this .cacheName + "'");
268: }
269: rawCache = this .cacheManager.getCache(this .cacheName);
270: } else {
271: if (logger.isDebugEnabled()) {
272: logger.debug("Creating new EHCache cache region '"
273: + this .cacheName + "'");
274: }
275: rawCache = createCache();
276: this .cacheManager.addCache(rawCache);
277: }
278:
279: // Decorate cache if necessary.
280: Ehcache decoratedCache = decorateCache(rawCache);
281: if (decoratedCache != rawCache) {
282: this .cacheManager.replaceCacheWithDecoratedCache(rawCache,
283: decoratedCache);
284: }
285: this .cache = decoratedCache;
286: }
287:
288: /**
289: * Create a raw Cache object based on the configuration of this FactoryBean.
290: */
291: private Cache createCache() {
292: return new Cache(this .cacheName, this .maxElementsInMemory,
293: this .memoryStoreEvictionPolicy, this .overflowToDisk,
294: this .diskStorePath, this .eternal, this .timeToLive,
295: this .timeToIdle, this .diskPersistent,
296: this .diskExpiryThreadIntervalSeconds, null, null,
297: this .maxElementsOnDisk);
298: }
299:
300: /**
301: * Decorate the given Cache, if necessary.
302: * <p>The default implementation simply returns the given cache object as-is.
303: * @param cache the raw Cache object, based on the configuration of this FactoryBean
304: * @return the (potentially decorated) cache object to be registered with the CacheManager
305: */
306: protected Ehcache decorateCache(Cache cache) {
307: if (this .cacheEntryFactory != null) {
308: if (this .cacheEntryFactory instanceof UpdatingCacheEntryFactory) {
309: return new UpdatingSelfPopulatingCache(
310: cache,
311: (UpdatingCacheEntryFactory) this .cacheEntryFactory);
312: } else {
313: return new SelfPopulatingCache(cache,
314: this .cacheEntryFactory);
315: }
316: }
317: if (this .blocking) {
318: return new BlockingCache(cache);
319: }
320: return cache;
321: }
322:
323: public Object getObject() {
324: return this .cache;
325: }
326:
327: public Class getObjectType() {
328: return (this .cache != null ? this .cache.getClass()
329: : Ehcache.class);
330: }
331:
332: public boolean isSingleton() {
333: return true;
334: }
335:
336: }
|