|
|||||||||||||||||||
Source file | Conditionals | Statements | Methods | TOTAL | |||||||||||||||
AbstractCacheAdministrator.java | 35,7% | 47,9% | 75% | 48,7% |
|
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: 1.1 $ | |
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 | 96 | protected AbstractCacheAdministrator(Properties p) { |
156 | 96 | loadProps(p); |
157 | 96 | initCacheParameters(); |
158 | ||
159 | 96 | 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 | 108 | public boolean isBlocking() { |
184 | 108 | 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 | 16 | protected void setCacheCapacity(int newCacheCapacity) { |
195 | 16 | 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 | 108 | public boolean isMemoryCaching() { |
206 | 108 | 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 | 396 | public String getProperty(String key) { |
216 | 396 | return config.getProperty(key); |
217 | } | |
218 | ||
219 | /** | |
220 | * Indicates whether the unlimited disk cache is enabled or not. | |
221 | */ | |
222 | 108 | public boolean isUnlimitedDiskCache() { |
223 | 108 | return unlimitedDiskCache; |
224 | } | |
225 | ||
226 | /** | |
227 | * Check if we use overflowPersistence | |
228 | * | |
229 | * @return Returns the overflowPersistence. | |
230 | */ | |
231 | 108 | public boolean isOverflowPersistence() { |
232 | 108 | 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 | 0 | protected CacheEventListener[] getCacheEventListeners() { |
249 | 0 | CacheEventListener[] listeners = null; |
250 | ||
251 | 0 | List classes = StringUtil.split(config.getProperty(CACHE_ENTRY_EVENT_LISTENERS_KEY), ','); |
252 | 0 | listeners = new CacheEventListener[classes.size()]; |
253 | ||
254 | 0 | for (int i = 0; i < classes.size(); i++) { |
255 | 0 | String className = (String) classes.get(i); |
256 | ||
257 | 0 | try { |
258 | 0 | Class clazz = Class.forName(className); |
259 | ||
260 | 0 | if (!CacheEventListener.class.isAssignableFrom(clazz)) { |
261 | 0 | log.error("Specified listener class '" + className + "' does not implement CacheEventListener. Ignoring this listener."); |
262 | } else { | |
263 | 0 | listeners[i] = (CacheEventListener) clazz.newInstance(); |
264 | } | |
265 | } catch (ClassNotFoundException e) { | |
266 | 0 | log.error("CacheEventListener class '" + className + "' not found. Ignoring this listener.", e); |
267 | } catch (InstantiationException e) { | |
268 | 0 | log.error("CacheEventListener class '" + className + "' could not be instantiated because it is not a concrete class. Ignoring this listener.", e); |
269 | } catch (IllegalAccessException e) { | |
270 | 0 | log.error("CacheEventListener class '" + className + "' could not be instantiated because it is not public. Ignoring this listener.", e); |
271 | } | |
272 | } | |
273 | ||
274 | 0 | return listeners; |
275 | } | |
276 | ||
277 | /** | |
278 | * If there is a <code>PersistenceListener</code> in the configuration | |
279 | * it will be instantiated and applied to the given cache object. If the | |
280 | * <code>PersistenceListener</code> cannot be found or instantiated, an | |
281 | * error will be logged but the cache will not have a persistence listener | |
282 | * applied to it and no exception will be thrown.<p> | |
283 | * | |
284 | * A cache can only have one <code>PersistenceListener</code>. | |
285 | * | |
286 | * @param cache the cache to apply the <code>PersistenceListener</code> to. | |
287 | * | |
288 | * @return the same cache object that was passed in. | |
289 | */ | |
290 | 54 | protected Cache setPersistenceListener(Cache cache) { |
291 | 54 | String persistenceClassname = config.getProperty(PERSISTENCE_CLASS_KEY); |
292 | ||
293 | 54 | try { |
294 | 54 | Class clazz = Class.forName(persistenceClassname); |
295 | 54 | PersistenceListener persistenceListener = (PersistenceListener) clazz.newInstance(); |
296 | ||
297 | 54 | cache.setPersistenceListener(persistenceListener.configure(config)); |
298 | } catch (ClassNotFoundException e) { | |
299 | 0 | log.error("PersistenceListener class '" + persistenceClassname + "' not found. Check your configuration.", e); |
300 | } catch (Exception e) { | |
301 | 0 | log.error("Error instantiating class '" + persistenceClassname + "'", e); |
302 | } | |
303 | ||
304 | 54 | return cache; |
305 | } | |
306 | ||
307 | /** | |
308 | * Applies all of the recognised listener classes to the supplied | |
309 | * cache object. Recognised classes are {@link CacheEntryEventListener} | |
310 | * and {@link CacheMapAccessEventListener}.<p> | |
311 | * | |
312 | * @param cache The cache to apply the configuration to. | |
313 | * @return cache The configured cache object. | |
314 | */ | |
315 | 96 | protected Cache configureStandardListeners(Cache cache) { |
316 | 96 | if (config.getProperty(PERSISTENCE_CLASS_KEY) != null) { |
317 | 54 | cache = setPersistenceListener(cache); |
318 | } | |
319 | ||
320 | 96 | if (config.getProperty(CACHE_ENTRY_EVENT_LISTENERS_KEY) != null) { |
321 | // Grab all the specified listeners and add them to the cache's | |
322 | // listener list. Note that listeners that implement more than | |
323 | // one of the event interfaces will be added multiple times. | |
324 | 0 | CacheEventListener[] listeners = getCacheEventListeners(); |
325 | ||
326 | 0 | for (int i = 0; i < listeners.length; i++) { |
327 | // Pass through the configuration to those listeners that require it | |
328 | 0 | if (listeners[i] instanceof LifecycleAware) { |
329 | 0 | try { |
330 | 0 | ((LifecycleAware) listeners[i]).initialize(cache, config); |
331 | } catch (InitializationException e) { | |
332 | 0 | log.error("Could not initialize listener '" + listeners[i].getClass().getName() + "'. Listener ignored.", e); |
333 | ||
334 | 0 | continue; |
335 | } | |
336 | } | |
337 | ||
338 | 0 | if (listeners[i] instanceof CacheEntryEventListener) { |
339 | 0 | cache.addCacheEventListener(listeners[i], CacheEntryEventListener.class); |
340 | } | |
341 | ||
342 | 0 | if (listeners[i] instanceof CacheMapAccessEventListener) { |
343 | 0 | cache.addCacheEventListener(listeners[i], CacheMapAccessEventListener.class); |
344 | } | |
345 | } | |
346 | } | |
347 | ||
348 | 96 | return cache; |
349 | } | |
350 | ||
351 | /** | |
352 | * Finalizes all the listeners that are associated with the given cache object. | |
353 | * Any <code>FinalizationException</code>s that are thrown by the listeners will | |
354 | * be caught and logged. | |
355 | */ | |
356 | 4 | protected void finalizeListeners(Cache cache) { |
357 | // It's possible for cache to be null if getCache() was never called (CACHE-63) | |
358 | 4 | if (cache == null) { |
359 | 0 | return; |
360 | } | |
361 | ||
362 | 4 | Object[] listeners = cache.listenerList.getListenerList(); |
363 | ||
364 | 4 | for (int i = listeners.length - 2; i >= 0; i -= 2) { |
365 | 0 | if (listeners[i + 1] instanceof LifecycleAware) { |
366 | 0 | try { |
367 | 0 | ((LifecycleAware) listeners[i + 1]).finialize(); |
368 | } catch (FinalizationException e) { | |
369 | 0 | log.error("Listener could not be finalized", e); |
370 | } | |
371 | } | |
372 | } | |
373 | } | |
374 | ||
375 | /** | |
376 | * Initialize the core cache parameters from the configuration properties. | |
377 | * The parameters that are initialized are: | |
378 | * <ul> | |
379 | * <li>the algorithm class ({@link #CACHE_ALGORITHM_KEY})</li> | |
380 | * <li>the cache size ({@link #CACHE_CAPACITY_KEY})</li> | |
381 | * <li>whether the cache is blocking or non-blocking ({@link #CACHE_BLOCKING_KEY})</li> | |
382 | * <li>whether caching to memory is enabled ({@link #CACHE_MEMORY_KEY})</li> | |
383 | * <li>whether the persistent cache is unlimited in size ({@link #CACHE_DISK_UNLIMITED_KEY})</li> | |
384 | * </ul> | |
385 | */ | |
386 | 96 | private void initCacheParameters() { |
387 | 96 | algorithmClass = getProperty(CACHE_ALGORITHM_KEY); |
388 | ||
389 | 96 | blocking = "true".equalsIgnoreCase(getProperty(CACHE_BLOCKING_KEY)); |
390 | ||
391 | 96 | String cacheMemoryStr = getProperty(CACHE_MEMORY_KEY); |
392 | ||
393 | 96 | if ((cacheMemoryStr != null) && cacheMemoryStr.equalsIgnoreCase("false")) { |
394 | 18 | memoryCaching = false; |
395 | } | |
396 | ||
397 | 96 | unlimitedDiskCache = Boolean.valueOf(config.getProperty(CACHE_DISK_UNLIMITED_KEY)).booleanValue(); |
398 | 96 | overflowPersistence = Boolean.valueOf(config.getProperty(CACHE_PERSISTENCE_OVERFLOW_KEY)).booleanValue(); |
399 | ||
400 | 96 | String cacheSize = getProperty(CACHE_CAPACITY_KEY); |
401 | ||
402 | 96 | try { |
403 | 96 | if ((cacheSize != null) && (cacheSize.length() > 0)) { |
404 | 52 | cacheCapacity = Integer.parseInt(cacheSize); |
405 | } | |
406 | } catch (NumberFormatException e) { | |
407 | 0 | log.error("The value supplied for the cache capacity, '" + cacheSize + "', is not a valid number. The cache capacity setting is being ignored."); |
408 | } | |
409 | } | |
410 | ||
411 | /** | |
412 | * Load the properties file from the classpath. | |
413 | */ | |
414 | 96 | private void loadProps(Properties p) { |
415 | 96 | config = new Config(p); |
416 | } | |
417 | } |
|