|
|||||||||||||||||||
Source file | Conditionals | Statements | Methods | TOTAL | |||||||||||||||
AbstractCacheAdministrator.java | 71,4% | 70% | 81,2% | 71,9% |
|
1 | /* | |
2 | * Copyright (c) 2002-2003 by OpenSymphony | |
3 | * All rights reserved. | |
4 | */ | |
5 | package com.opensymphony.oscache.base; | |
6 | ||
7 | import com.opensymphony.oscache.base.algorithm.AbstractConcurrentReadCache; | |
8 | import com.opensymphony.oscache.base.events.*; | |
9 | import com.opensymphony.oscache.base.persistence.PersistenceListener; | |
10 | import com.opensymphony.oscache.util.StringUtil; | |
11 | ||
12 | import org.apache.commons.logging.Log; | |
13 | import org.apache.commons.logging.LogFactory; | |
14 | ||
15 | import java.util.*; | |
16 | ||
17 | import javax.swing.event.EventListenerList; | |
18 | ||
19 | /** | |
20 | * An AbstractCacheAdministrator defines an abstract cache administrator, implementing all | |
21 | * the basic operations related to the configuration of a cache, including assigning | |
22 | * any configured event handlers to cache objects.<p> | |
23 | * | |
24 | * Extend this class to implement a custom cache administrator. | |
25 | * | |
26 | * @version $Revision: 425 $ | |
27 | * @author a href="mailto:mike@atlassian.com">Mike Cannon-Brookes</a> | |
28 | * @author <a href="mailto:fbeauregard@pyxis-tech.com">Francois Beauregard</a> | |
29 | * @author <a href="mailto:abergevin@pyxis-tech.com">Alain Bergevin</a> | |
30 | * @author <a href="mailto:fabian.crabus@gurulogic.de">Fabian Crabus</a> | |
31 | * @author <a href="mailto:chris@swebtec.com">Chris Miller</a> | |
32 | */ | |
33 | public abstract class AbstractCacheAdministrator implements java.io.Serializable { | |
34 | private static transient final Log log = LogFactory.getLog(AbstractCacheAdministrator.class); | |
35 | ||
36 | /** | |
37 | * A boolean cache configuration property that indicates whether the cache | |
38 | * should cache objects in memory. Set this property to <code>false</code> | |
39 | * to disable in-memory caching. | |
40 | */ | |
41 | public final static String CACHE_MEMORY_KEY = "cache.memory"; | |
42 | ||
43 | /** | |
44 | * An integer cache configuration property that specifies the maximum number | |
45 | * of objects to hold in the cache. Setting this to a negative value will | |
46 | * disable the capacity functionality - there will be no limit to the number | |
47 | * of objects that are held in cache. | |
48 | */ | |
49 | public final static String CACHE_CAPACITY_KEY = "cache.capacity"; | |
50 | ||
51 | /** | |
52 | * A String cache configuration property that specifies the classname of | |
53 | * an alternate caching algorithm. This class must extend | |
54 | * {@link com.opensymphony.oscache.base.algorithm.AbstractConcurrentReadCache} | |
55 | * By default caches will use {@link com.opensymphony.oscache.base.algorithm.LRUCache} as | |
56 | * the default algorithm if the cache capacity is set to a postive value, or | |
57 | * {@link com.opensymphony.oscache.base.algorithm.UnlimitedCache} if the | |
58 | * capacity is negative (ie, disabled). | |
59 | */ | |
60 | public final static String CACHE_ALGORITHM_KEY = "cache.algorithm"; | |
61 | ||
62 | /** | |
63 | * A boolean cache configuration property that indicates whether the persistent | |
64 | * cache should be unlimited in size, or should be restricted to the same size | |
65 | * as the in-memory cache. Set this property to <code>true</code> to allow the | |
66 | * persistent cache to grow without bound. | |
67 | */ | |
68 | public final static String CACHE_DISK_UNLIMITED_KEY = "cache.unlimited.disk"; | |
69 | ||
70 | /** | |
71 | * The configuration key that specifies whether we should block waiting for new | |
72 | * content to be generated, or just serve the old content instead. The default | |
73 | * behaviour is to serve the old content since that provides the best performance | |
74 | * (at the cost of serving slightly stale data). | |
75 | */ | |
76 | public final static String CACHE_BLOCKING_KEY = "cache.blocking"; | |
77 | ||
78 | /** | |
79 | * A String cache configuration property that specifies the classname that will | |
80 | * be used to provide cache persistence. This class must extend {@link PersistenceListener}. | |
81 | */ | |
82 | public static final String PERSISTENCE_CLASS_KEY = "cache.persistence.class"; | |
83 | ||
84 | /** | |
85 | * A String cache configuration property that specifies if the cache persistence | |
86 | * will only be used in overflow mode, that is, when the memory cache capacity has been reached. | |
87 | */ | |
88 | public static final String CACHE_PERSISTENCE_OVERFLOW_KEY = "cache.persistence.overflow.only"; | |
89 | ||
90 | /** | |
91 | * A String cache configuration property that holds a comma-delimited list of | |
92 | * classnames. These classes specify the event handlers that are to be applied | |
93 | * to the cache. | |
94 | */ | |
95 | public static final String CACHE_ENTRY_EVENT_LISTENERS_KEY = "cache.event.listeners"; | |
96 | protected Config config = null; | |
97 | ||
98 | /** | |
99 | * Holds a list of all the registered event listeners. Event listeners are specified | |
100 | * using the {@link #CACHE_ENTRY_EVENT_LISTENERS_KEY} configuration key. | |
101 | */ | |
102 | protected EventListenerList listenerList = new EventListenerList(); | |
103 | ||
104 | /** | |
105 | * The algorithm class being used, as specified by the {@link #CACHE_ALGORITHM_KEY} | |
106 | * configuration property. | |
107 | */ | |
108 | protected String algorithmClass = null; | |
109 | ||
110 | /** | |
111 | * The cache capacity (number of entries), as specified by the {@link #CACHE_CAPACITY_KEY} | |
112 | * configuration property. | |
113 | */ | |
114 | protected int cacheCapacity = -1; | |
115 | ||
116 | /** | |
117 | * Whether the cache blocks waiting for content to be build, or serves stale | |
118 | * content instead. This value can be specified using the {@link #CACHE_BLOCKING_KEY} | |
119 | * configuration property. | |
120 | */ | |
121 | private boolean blocking = false; | |
122 | ||
123 | /** | |
124 | * Whether or not to store the cache entries in memory. This is configurable using the | |
125 | * {@link com.opensymphony.oscache.base.AbstractCacheAdministrator#CACHE_MEMORY_KEY} property. | |
126 | */ | |
127 | private boolean memoryCaching = true; | |
128 | ||
129 | /** | |
130 | * Whether the persistent cache should be used immediately or only when the memory capacity | |
131 | * has been reached, ie. overflow only. | |
132 | * This can be set via the {@link #CACHE_PERSISTENCE_OVERFLOW_KEY} configuration property. | |
133 | */ | |
134 | private boolean overflowPersistence; | |
135 | ||
136 | /** | |
137 | * Whether the disk cache should be unlimited in size, or matched 1-1 to the memory cache. | |
138 | * This can be set via the {@link #CACHE_DISK_UNLIMITED_KEY} configuration property. | |
139 | */ | |
140 | private boolean unlimitedDiskCache; | |
141 | ||
142 | /** | |
143 | * Create the AbstractCacheAdministrator. | |
144 | * This will initialize all values and load the properties from oscache.properties. | |
145 | */ | |
146 | 0 | protected AbstractCacheAdministrator() { |
147 | 0 | this(null); |
148 | } | |
149 | ||
150 | /** | |
151 | * Create the AbstractCacheAdministrator. | |
152 | * | |
153 | * @param p the configuration properties for this cache. | |
154 | */ | |
155 | 195 | protected AbstractCacheAdministrator(Properties p) { |
156 | 195 | loadProps(p); |
157 | 195 | initCacheParameters(); |
158 | ||
159 | 195 | if (log.isDebugEnabled()) { |
160 | 0 | log.debug("Constructed AbstractCacheAdministrator()"); |
161 | } | |
162 | } | |
163 | ||
164 | /** | |
165 | * Sets the algorithm to use for the cache. | |
166 | * | |
167 | * @see com.opensymphony.oscache.base.algorithm.LRUCache | |
168 | * @see com.opensymphony.oscache.base.algorithm.FIFOCache | |
169 | * @see com.opensymphony.oscache.base.algorithm.UnlimitedCache | |
170 | * @param newAlgorithmClass The class to use (eg. | |
171 | * <code>"com.opensymphony.oscache.base.algorithm.LRUCache"</code>) | |
172 | */ | |
173 | 0 | public void setAlgorithmClass(String newAlgorithmClass) { |
174 | 0 | algorithmClass = newAlgorithmClass; |
175 | } | |
176 | ||
177 | /** | |
178 | * Indicates whether the cache will block waiting for new content to | |
179 | * be built, or serve stale content instead of waiting. Regardless of this | |
180 | * setting, the cache will <em>always</em> block if new content is being | |
181 | * created, ie, there's no stale content in the cache that can be served. | |
182 | */ | |
183 | 225 | public boolean isBlocking() { |
184 | 225 | return blocking; |
185 | } | |
186 | ||
187 | /** | |
188 | * Sets the cache capacity (number of items). Administrator implementations | |
189 | * should override this method to ensure that their {@link Cache} objects | |
190 | * are updated correctly (by calling {@link AbstractConcurrentReadCache#setMaxEntries(int)}}}. | |
191 | * | |
192 | * @param newCacheCapacity The new capacity | |
193 | */ | |
194 | 20 | protected void setCacheCapacity(int newCacheCapacity) { |
195 | 20 | cacheCapacity = newCacheCapacity; |
196 | } | |
197 | ||
198 | /** | |
199 | * Whether entries are cached in memory or not. | |
200 | * Default is true. | |
201 | * Set by the <code>cache.memory</code> property. | |
202 | * | |
203 | * @return Status whether or not memory caching is used. | |
204 | */ | |
205 | 235 | public boolean isMemoryCaching() { |
206 | 235 | return memoryCaching; |
207 | } | |
208 | ||
209 | /** | |
210 | * Retrieves the value of one of the configuration properties. | |
211 | * | |
212 | * @param key The key assigned to the property | |
213 | * @return Property value, or <code>null</code> if the property could not be found. | |
214 | */ | |
215 | 795 | public String getProperty(String key) { |
216 | 795 | return config.getProperty(key); |
217 | } | |
218 | ||
219 | /** | |
220 | * Indicates whether the unlimited disk cache is enabled or not. | |
221 | */ | |
222 | 235 | public boolean isUnlimitedDiskCache() { |
223 | 235 | return unlimitedDiskCache; |
224 | } | |
225 | ||
226 | /** | |
227 | * Check if we use overflowPersistence | |
228 | * | |
229 | * @return Returns the overflowPersistence. | |
230 | */ | |
231 | 235 | public boolean isOverflowPersistence() { |
232 | 235 | return this.overflowPersistence; |
233 | } | |
234 | ||
235 | /** | |
236 | * Sets the overflowPersistence flag | |
237 | * | |
238 | * @param overflowPersistence The overflowPersistence to set. | |
239 | */ | |
240 | 0 | public void setOverflowPersistence(boolean overflowPersistence) { |
241 | 0 | this.overflowPersistence = overflowPersistence; |
242 | } | |
243 | ||
244 | /** | |
245 | * Retrieves an array containing instances all of the {@link CacheEventListener} | |
246 | * classes that are specified in the OSCache configuration file. | |
247 | */ | |
248 | 29 | protected CacheEventListener[] getCacheEventListeners() { |
249 | 29 | List classes = StringUtil.split(config.getProperty(CACHE_ENTRY_EVENT_LISTENERS_KEY), ','); |
250 | 29 | CacheEventListener[] listeners = new CacheEventListener[classes.size()]; |
251 | ||
252 | 29 | for (int i = 0; i < classes.size(); i++) { |
253 | 29 | String className = (String) classes.get(i); |
254 | ||
255 | 29 | try { |
256 | 29 | Class clazz = Class.forName(className); |
257 | ||
258 | 29 | if (!CacheEventListener.class.isAssignableFrom(clazz)) { |
259 | 0 | log.error("Specified listener class '" + className + "' does not implement CacheEventListener. Ignoring this listener."); |
260 | } else { | |
261 | 29 | listeners[i] = (CacheEventListener) clazz.newInstance(); |
262 | } | |
263 | } catch (ClassNotFoundException e) { | |
264 | 0 | log.error("CacheEventListener class '" + className + "' not found. Ignoring this listener.", e); |
265 | } catch (InstantiationException e) { | |
266 | 0 | log.error("CacheEventListener class '" + className + "' could not be instantiated because it is not a concrete class. Ignoring this listener.", e); |
267 | } catch (IllegalAccessException e) { | |
268 | 0 | log.error("CacheEventListener class '" + className + "' could not be instantiated because it is not public. Ignoring this listener.", e); |
269 | } | |
270 | } | |
271 | ||
272 | 29 | return listeners; |
273 | } | |
274 | ||
275 | /** | |
276 | * If there is a <code>PersistenceListener</code> in the configuration | |
277 | * it will be instantiated and applied to the given cache object. If the | |
278 | * <code>PersistenceListener</code> cannot be found or instantiated, an | |
279 | * error will be logged but the cache will not have a persistence listener | |
280 | * applied to it and no exception will be thrown.<p> | |
281 | * | |
282 | * A cache can only have one <code>PersistenceListener</code>. | |
283 | * | |
284 | * @param cache the cache to apply the <code>PersistenceListener</code> to. | |
285 | * | |
286 | * @return the same cache object that was passed in. | |
287 | */ | |
288 | 116 | protected Cache setPersistenceListener(Cache cache) { |
289 | 116 | String persistenceClassname = config.getProperty(PERSISTENCE_CLASS_KEY); |
290 | ||
291 | 116 | try { |
292 | 116 | Class clazz = Class.forName(persistenceClassname); |
293 | 116 | PersistenceListener persistenceListener = (PersistenceListener) clazz.newInstance(); |
294 | ||
295 | 116 | cache.setPersistenceListener(persistenceListener.configure(config)); |
296 | } catch (ClassNotFoundException e) { | |
297 | 0 | log.error("PersistenceListener class '" + persistenceClassname + "' not found. Check your configuration.", e); |
298 | } catch (Exception e) { | |
299 | 0 | log.error("Error instantiating class '" + persistenceClassname + "'", e); |
300 | } | |
301 | ||
302 | 116 | return cache; |
303 | } | |
304 | ||
305 | /** | |
306 | * Applies all of the recognised listener classes to the supplied | |
307 | * cache object. Recognised classes are {@link CacheEntryEventListener} | |
308 | * and {@link CacheMapAccessEventListener}.<p> | |
309 | * | |
310 | * @param cache The cache to apply the configuration to. | |
311 | * @return cache The configured cache object. | |
312 | */ | |
313 | 195 | protected Cache configureStandardListeners(Cache cache) { |
314 | 195 | if (config.getProperty(PERSISTENCE_CLASS_KEY) != null) { |
315 | 116 | cache = setPersistenceListener(cache); |
316 | } | |
317 | ||
318 | 195 | if (config.getProperty(CACHE_ENTRY_EVENT_LISTENERS_KEY) != null) { |
319 | // Grab all the specified listeners and add them to the cache's | |
320 | // listener list. Note that listeners that implement more than | |
321 | // one of the event interfaces will be added multiple times. | |
322 | 29 | CacheEventListener[] listeners = getCacheEventListeners(); |
323 | ||
324 | 29 | for (int i = 0; i < listeners.length; i++) { |
325 | // Pass through the configuration to those listeners that require it | |
326 | 29 | if (listeners[i] instanceof LifecycleAware) { |
327 | 0 | try { |
328 | 0 | ((LifecycleAware) listeners[i]).initialize(cache, config); |
329 | } catch (InitializationException e) { | |
330 | 0 | log.error("Could not initialize listener '" + listeners[i].getClass().getName() + "'. Listener ignored.", e); |
331 | ||
332 | 0 | continue; |
333 | } | |
334 | } | |
335 | ||
336 | 29 | if (listeners[i] instanceof CacheEntryEventListener) { |
337 | 29 | cache.addCacheEventListener(listeners[i]); |
338 | 0 | } else if (listeners[i] instanceof CacheMapAccessEventListener) { |
339 | 0 | cache.addCacheEventListener(listeners[i]); |
340 | } | |
341 | } | |
342 | } | |
343 | ||
344 | 195 | return cache; |
345 | } | |
346 | ||
347 | /** | |
348 | * Finalizes all the listeners that are associated with the given cache object. | |
349 | * Any <code>FinalizationException</code>s that are thrown by the listeners will | |
350 | * be caught and logged. | |
351 | */ | |
352 | 10 | protected void finalizeListeners(Cache cache) { |
353 | // It's possible for cache to be null if getCache() was never called (CACHE-63) | |
354 | 10 | if (cache == null) { |
355 | 0 | return; |
356 | } | |
357 | ||
358 | 10 | Object[] listeners = cache.listenerList.getListenerList(); |
359 | ||
360 | 10 | for (int i = listeners.length - 2; i >= 0; i -= 2) { |
361 | 1 | if (listeners[i + 1] instanceof LifecycleAware) { |
362 | 0 | try { |
363 | 0 | ((LifecycleAware) listeners[i + 1]).finialize(); |
364 | } catch (FinalizationException e) { | |
365 | 0 | log.error("Listener could not be finalized", e); |
366 | } | |
367 | } | |
368 | } | |
369 | } | |
370 | ||
371 | /** | |
372 | * Initialize the core cache parameters from the configuration properties. | |
373 | * The parameters that are initialized are: | |
374 | * <ul> | |
375 | * <li>the algorithm class ({@link #CACHE_ALGORITHM_KEY})</li> | |
376 | * <li>the cache size ({@link #CACHE_CAPACITY_KEY})</li> | |
377 | * <li>whether the cache is blocking or non-blocking ({@link #CACHE_BLOCKING_KEY})</li> | |
378 | * <li>whether caching to memory is enabled ({@link #CACHE_MEMORY_KEY})</li> | |
379 | * <li>whether the persistent cache is unlimited in size ({@link #CACHE_DISK_UNLIMITED_KEY})</li> | |
380 | * </ul> | |
381 | */ | |
382 | 195 | private void initCacheParameters() { |
383 | 195 | algorithmClass = getProperty(CACHE_ALGORITHM_KEY); |
384 | ||
385 | 195 | blocking = "true".equalsIgnoreCase(getProperty(CACHE_BLOCKING_KEY)); |
386 | ||
387 | 195 | String cacheMemoryStr = getProperty(CACHE_MEMORY_KEY); |
388 | ||
389 | 195 | if ((cacheMemoryStr != null) && cacheMemoryStr.equalsIgnoreCase("false")) { |
390 | 58 | memoryCaching = false; |
391 | } | |
392 | ||
393 | 195 | unlimitedDiskCache = Boolean.valueOf(config.getProperty(CACHE_DISK_UNLIMITED_KEY)).booleanValue(); |
394 | 195 | overflowPersistence = Boolean.valueOf(config.getProperty(CACHE_PERSISTENCE_OVERFLOW_KEY)).booleanValue(); |
395 | ||
396 | 195 | String cacheSize = getProperty(CACHE_CAPACITY_KEY); |
397 | ||
398 | 195 | try { |
399 | 195 | if ((cacheSize != null) && (cacheSize.length() > 0)) { |
400 | 88 | cacheCapacity = Integer.parseInt(cacheSize); |
401 | } | |
402 | } catch (NumberFormatException e) { | |
403 | 0 | log.error("The value supplied for the cache capacity, '" + cacheSize + "', is not a valid number. The cache capacity setting is being ignored."); |
404 | } | |
405 | } | |
406 | ||
407 | /** | |
408 | * Load the properties file from the classpath. | |
409 | */ | |
410 | 195 | private void loadProps(Properties p) { |
411 | 195 | config = new Config(p); |
412 | } | |
413 | } |
|