File Source: CacheManager.java
1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. The ASF licenses this file to You
4 * under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License. For additional information regarding
15 * copyright in this work, please see the NOTICE file in the top level
16 * directory of this distribution.
17 */
18
19 package org.apache.roller.weblogger.util.cache;
20
21 import java.util.Date;
22 import java.util.HashMap;
23 import java.util.HashSet;
24 import java.util.Iterator;
25 import java.util.Map;
26 import java.util.Set;
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29 import org.apache.roller.weblogger.WebloggerException;
30 import org.apache.roller.weblogger.business.runnable.ContinuousWorkerThread;
31 import org.apache.roller.weblogger.business.runnable.Job;
32 import org.apache.roller.weblogger.config.WebloggerConfig;
33 import org.apache.roller.weblogger.business.WebloggerFactory;
34 import org.apache.roller.weblogger.business.UserManager;
35 import org.apache.roller.weblogger.pojos.WeblogBookmark;
36 import org.apache.roller.weblogger.pojos.WeblogEntryComment;
37 import org.apache.roller.weblogger.pojos.WeblogBookmarkFolder;
38 import org.apache.roller.weblogger.pojos.WeblogReferrer;
39 import org.apache.roller.weblogger.pojos.User;
40 import org.apache.roller.weblogger.pojos.WeblogCategory;
41 import org.apache.roller.weblogger.pojos.WeblogEntry;
42 import org.apache.roller.weblogger.pojos.WeblogTemplate;
43 import org.apache.roller.weblogger.pojos.Weblog;
44
45
46 /**
47 * A governing class for Roller cache objects.
48 *
49 * The purpose of the CacheManager is to provide a level of abstraction between
50 * classes that use a cache and the implementations of a cache. This allows
51 * us to create easily pluggable cache implementations.
52 *
53 * The other purpose is to provide a single interface for interacting with all
54 * Roller caches at the same time. This is beneficial because as data
55 * changes in the system we often need to notify all caches that some part of
56 * their cached data needs to be invalidated, and the CacheManager makes that
57 * process easier.
58 */
59 public class CacheManager {
60
/*
P/P * Method: org.apache.roller.weblogger.util.cache.CacheManager__static_init
*
* Presumptions:
* java.lang.Class:forName(...)@82 != null
* java.lang.Class:forName(...)@95 != null
* java.lang.Object:getClass(...)@103 != null
* org.apache.commons.logging.LogFactory:getLog(...)@61 != null
*
* Postconditions:
* " falling back on default"._tainted == 0
* "Cache Factory = "._tainted == 0
* "Unable to instantiate cache factory ["._tainted == 0
* "Unable to instantiate cache handler ["._tainted == 0
* "]"._tainted == 0
* cacheFactory != null
* cacheHandlers == &new HashSet(CacheManager__static_init#1)
* caches == &new HashMap(CacheManager__static_init#2)
* (soft) log != null
* new HashMap(CacheManager__static_init#2) num objects == 1
* ...
*
* Test Vectors:
* java.lang.String:length(...)@108: {0}, {1..232-1}
* org.apache.roller.weblogger.config.WebloggerConfig:getProperty(...)@107: Addr_Set{null}, Inverse{null}
*/
61 private static Log log = LogFactory.getLog(CacheManager.class);
62
63 private static final String DEFAULT_FACTORY =
64 "org.apache.roller.weblogger.util.cache.ExpiringLRUCacheFactoryImpl";
65
66 // a reference to the cache factory in use
67 private static CacheFactory cacheFactory = null;
68
69 // a set of all registered cache handlers
70 private static Set cacheHandlers = new HashSet();
71
72 // a map of all registered caches
73 private static Map caches = new HashMap();
74
75
76 static {
77 // lookup what cache factory we want to use
78 String classname = WebloggerConfig.getProperty("cache.defaultFactory");
79
80 // use reflection to instantiate our factory class
81 try {
82 Class factoryClass = Class.forName(classname);
83 cacheFactory = (CacheFactory) factoryClass.newInstance();
84 } catch(ClassCastException cce) {
85 log.error("It appears that your factory does not implement "+
86 "the CacheFactory interface",cce);
87 } catch(Exception e) {
88 log.error("Unable to instantiate cache factory ["+classname+"]"+
89 " falling back on default", e);
90 }
91
92 if(cacheFactory == null) try {
93 // hmm ... failed to load the specified cache factory
94 // lets try our default
95 Class factoryClass = Class.forName(DEFAULT_FACTORY);
96 cacheFactory = (CacheFactory) factoryClass.newInstance();
97 } catch(Exception e) {
98 log.fatal("Failed to instantiate a cache factory", e);
99 throw new RuntimeException(e);
100 }
101
102 log.info("Cache Manager Initialized.");
103 log.info("Cache Factory = "+cacheFactory.getClass().getName());
104
105
106 // add custom handlers
107 String customHandlers = WebloggerConfig.getProperty("cache.customHandlers");
108 if(customHandlers != null && customHandlers.trim().length() > 0) {
109
110 String[] cHandlers = customHandlers.split(",");
+ 111 for(int i=0; i < cHandlers.length; i++) {
112 // use reflection to instantiate the handler class
113 try {
+ 114 Class handlerClass = Class.forName(cHandlers[i]);
115 CacheHandler customHandler =
116 (CacheHandler) handlerClass.newInstance();
117
118 cacheHandlers.add(customHandler);
119 } catch(ClassCastException cce) {
120 log.error("It appears that your handler does not implement "+
121 "the CacheHandler interface",cce);
122 } catch(Exception e) {
123 log.error("Unable to instantiate cache handler ["+cHandlers[i]+"]", e);
124 }
125 }
126 }
127 }
128
129
130 // a non-instantiable class
/*
P/P * Method: void org.apache.roller.weblogger.util.cache.CacheManager()
*/
131 private CacheManager() {}
132
133
134 /**
135 * Ask the CacheManager to construct a cache.
136 *
137 * Normally the CacheManager will use whatever CacheFactory has been
138 * chosen for the system via the cache.defaultFactory property.
139 * However, it is possible to override the use of the default factory by
140 * supplying a "factory" property to this method. The value should
141 * be the full classname for the factory you want to use for constructing
142 * the cache.
143 *
144 * example:
145 * factory -> org.apache.roller.weblogger.util.cache.LRUCacheFactoryImpl
146 *
147 * This allows Roller admins the ability to choose a caching strategy to
148 * use for the whole system, but override it in certain places where they
149 * see fit. It also allows users to write their own caching modifications
150 * and have them used only by specific caches.
151 */
152 public static Cache constructCache(CacheHandler handler, Map properties) {
153
/*
P/P * Method: Cache constructCache(CacheHandler, Map)
*
* Preconditions:
* caches != null
* log != null
* properties != null
* (soft) cacheFactory != null
* (soft) cacheHandlers != null
*
* Presumptions:
* java.lang.Class:forName(...)@164 != null
*
* Postconditions:
* return_value != null
* init'ed(new Date(LRUCacheImpl#1) num objects)
* init'ed(new ExpiringLRUCacheImpl(constructCache#1*) num objects)
* possibly_updated(new ExpiringLRUCacheImpl(constructCache#1*).cache)
* possibly_updated(new ExpiringLRUCacheImpl(constructCache#1*).hits)
* possibly_updated(new ExpiringLRUCacheImpl(constructCache#1*).id)
* possibly_updated(new ExpiringLRUCacheImpl(constructCache#1*).misses)
* possibly_updated(new ExpiringLRUCacheImpl(constructCache#1*).puts)
* possibly_updated(new ExpiringLRUCacheImpl(constructCache#1*).removes)
* possibly_updated(new ExpiringLRUCacheImpl(constructCache#1*).startTime)
* ...
*
* Test Vectors:
* handler: Addr_Set{null}, Inverse{null}
* java.util.Map:containsKey(...)@158: {0}, {1}
*/
154 log.debug("Constructing new cache with props "+properties);
155
156 Cache cache = null;
157
+ 158 if(properties != null && properties.containsKey("factory")) {
159 // someone wants a custom cache instance
160 String classname = (String) properties.get("factory");
161
162 try {
163 // use reflection to instantiate the factory class
164 Class factoryClass = Class.forName(classname);
165 CacheFactory factory = (CacheFactory) factoryClass.newInstance();
166
167 // now ask for a new cache
168 cache = factory.constructCache(properties);
169 } catch(ClassCastException cce) {
170 log.error("It appears that your factory ["+classname+
171 "] does not implement the CacheFactory interface",cce);
172 } catch(Exception e) {
173 log.error("Unable to instantiate cache factory ["+classname+
174 "] falling back on default", e);
175 }
176 }
177
178 if(cache == null) {
179 // ask our default cache factory for a new cache instance
180 cache = cacheFactory.constructCache(properties);
181 }
182
+ 183 if(cache != null) {
184 caches.put(cache.getId(), cache);
185
186 // register the handler for this new cache
187 if(handler != null) {
188 cacheHandlers.add(handler);
189 }
190 }
191
192 return cache;
193 }
194
195
196 /**
197 * Register a CacheHandler to listen for object invalidations.
198 *
199 * This is here so that it's possible to to add classes which would respond
200 * to object invalidations without necessarily having to create a cache.
201 *
202 * An example would be a handler designed to notify other machines in a
203 * cluster when an object has been invalidated, or possibly the search
204 * index management classes are interested in knowing when objects are
205 * invalidated.
206 */
207 public static void registerHandler(CacheHandler handler) {
208
/*
P/P * Method: void registerHandler(CacheHandler)
*
* Preconditions:
* log != null
* (soft) cacheHandlers != null
*
* Test Vectors:
* handler: Addr_Set{null}, Inverse{null}
*/
209 log.debug("Registering handler "+handler);
210
211 if(handler != null) {
212 cacheHandlers.add(handler);
213 }
214 }
215
216
217 public static void invalidate(WeblogEntry entry) {
218
/*
P/P * Method: void invalidate(WeblogEntry)
*
* Preconditions:
* cacheHandlers != null
* entry != null
* log != null
*
* Presumptions:
* java.util.Iterator:next(...)@223 != null
*
* Test Vectors:
* java.util.Iterator:hasNext(...)@222: {0}, {1}
*/
219 log.debug("invalidating entry = "+entry.getAnchor());
220
221 Iterator handlers = cacheHandlers.iterator();
222 while(handlers.hasNext()) {
223 ((CacheHandler) handlers.next()).invalidate(entry);
224 }
225 }
226
227
228 public static void invalidate(Weblog website) {
229
/*
P/P * Method: void invalidate(Weblog)
*
* Preconditions:
* cacheHandlers != null
* log != null
* website != null
*
* Presumptions:
* java.util.Iterator:next(...)@234 != null
*
* Test Vectors:
* java.util.Iterator:hasNext(...)@233: {0}, {1}
*/
230 log.debug("invalidating website = "+website.getHandle());
231
232 Iterator handlers = cacheHandlers.iterator();
233 while(handlers.hasNext()) {
234 ((CacheHandler) handlers.next()).invalidate(website);
235 }
236 }
237
238
239 public static void invalidate(WeblogBookmark bookmark) {
240
/*
P/P * Method: void invalidate(WeblogBookmark)
*
* Preconditions:
* bookmark != null
* cacheHandlers != null
* log != null
*
* Presumptions:
* java.util.Iterator:next(...)@245 != null
*
* Test Vectors:
* java.util.Iterator:hasNext(...)@244: {0}, {1}
*/
241 log.debug("invalidating bookmark = "+bookmark.getId());
242
243 Iterator handlers = cacheHandlers.iterator();
244 while(handlers.hasNext()) {
245 ((CacheHandler) handlers.next()).invalidate(bookmark);
246 }
247 }
248
249
250 public static void invalidate(WeblogBookmarkFolder folder) {
251
/*
P/P * Method: void invalidate(WeblogBookmarkFolder)
*
* Preconditions:
* cacheHandlers != null
* folder != null
* log != null
*
* Presumptions:
* java.util.Iterator:next(...)@256 != null
*
* Test Vectors:
* java.util.Iterator:hasNext(...)@255: {0}, {1}
*/
252 log.debug("invalidating folder = "+folder.getId());
253
254 Iterator handlers = cacheHandlers.iterator();
255 while(handlers.hasNext()) {
256 ((CacheHandler) handlers.next()).invalidate(folder);
257 }
258 }
259
260
261 public static void invalidate(WeblogEntryComment comment) {
262
/*
P/P * Method: void invalidate(WeblogEntryComment)
*
* Preconditions:
* cacheHandlers != null
* comment != null
* log != null
*
* Presumptions:
* java.util.Iterator:next(...)@267 != null
*
* Test Vectors:
* java.util.Iterator:hasNext(...)@266: {0}, {1}
*/
263 log.debug("invalidating comment = "+comment.getId());
264
265 Iterator handlers = cacheHandlers.iterator();
266 while(handlers.hasNext()) {
267 ((CacheHandler) handlers.next()).invalidate(comment);
268 }
269 }
270
271
272 public static void invalidate(WeblogReferrer referer) {
273
/*
P/P * Method: void invalidate(WeblogReferrer)
*
* Preconditions:
* cacheHandlers != null
* log != null
* referer != null
*
* Presumptions:
* java.util.Iterator:next(...)@282 != null
*
* Test Vectors:
* java.util.Iterator:hasNext(...)@281: {0}, {1}
*/
274 log.debug("invalidating referer = "+referer.getId());
275
276 // NOTE: Invalidating an entire website for each referer is not
277 // good for our caching. This may need reevaluation later.
278 //lastExpiredCache.put(referer.getWebsite().getHandle(), new Date());
279
280 Iterator handlers = cacheHandlers.iterator();
281 while(handlers.hasNext()) {
282 ((CacheHandler) handlers.next()).invalidate(referer);
283 }
284 }
285
286
287 public static void invalidate(User user) {
288
/*
P/P * Method: void invalidate(User)
*
* Preconditions:
* cacheHandlers != null
* log != null
* user != null
*
* Presumptions:
* java.util.Iterator:next(...)@293 != null
*
* Test Vectors:
* java.util.Iterator:hasNext(...)@292: {0}, {1}
*/
289 log.debug("invalidating user = "+user.getUserName());
290
291 Iterator handlers = cacheHandlers.iterator();
292 while(handlers.hasNext()) {
293 ((CacheHandler) handlers.next()).invalidate(user);
294 }
295 }
296
297
298 public static void invalidate(WeblogCategory category) {
299
/*
P/P * Method: void invalidate(WeblogCategory)
*
* Preconditions:
* cacheHandlers != null
* category != null
* log != null
*
* Presumptions:
* java.util.Iterator:next(...)@304 != null
*
* Test Vectors:
* java.util.Iterator:hasNext(...)@303: {0}, {1}
*/
300 log.debug("invalidating category = "+category.getId());
301
302 Iterator handlers = cacheHandlers.iterator();
303 while(handlers.hasNext()) {
304 ((CacheHandler) handlers.next()).invalidate(category);
305 }
306 }
307
308
309 public static void invalidate(WeblogTemplate template) {
310
/*
P/P * Method: void invalidate(WeblogTemplate)
*
* Preconditions:
* cacheHandlers != null
* log != null
* template != null
*
* Presumptions:
* java.util.Iterator:next(...)@315 != null
*
* Test Vectors:
* java.util.Iterator:hasNext(...)@314: {0}, {1}
*/
311 log.debug("invalidating template = "+template.getId());
312
313 Iterator handlers = cacheHandlers.iterator();
314 while(handlers.hasNext()) {
315 ((CacheHandler) handlers.next()).invalidate(template);
316 }
317 }
318
319
320 /**
321 * Flush the entire cache system.
322 */
323 public static void clear() {
324
325 // loop through all caches and trigger a clear
/*
P/P * Method: void clear()
*
* Preconditions:
* caches != null
*
* Presumptions:
* cache.cache@329 != null
* java.util.Iterator:next(...)@329 != null
* java.util.Map:values(...)@327 != null
*
* Postconditions:
* init'ed(new Date(clear#1*) num objects)
*
* Test Vectors:
* java.util.Iterator:hasNext(...)@328: {0}, {1}
*/
326 Cache cache = null;
327 Iterator cachesIT = caches.values().iterator();
328 while(cachesIT.hasNext()) {
329 cache = (Cache) cachesIT.next();
330
331 cache.clear();
332 }
333 }
334
335
336 /**
337 * Flush a single cache.
338 */
339 public static void clear(String cacheId) {
340
/*
P/P * Method: void clear(String)
*
* Preconditions:
* caches != null
*
* Presumptions:
* cache.cache@341 != null
*
* Postconditions:
* new Date(clear#1*) num objects <= 1
*
* Test Vectors:
* java.util.Map:get(...)@341: Addr_Set{null}, Inverse{null}
*/
341 Cache cache = (Cache) caches.get(cacheId);
342 if(cache != null) {
343 cache.clear();
344 }
345 }
346
347
348 /**
349 * Compile stats from all registered caches.
350 *
351 * This is basically a hacky version of instrumentation which is being
352 * thrown in because we don't have a full instrumentation strategy yet.
353 * This is here with the full expectation that it will be replaced by
354 * something a bit more elaborate, like JMX.
355 */
356 public static Map getStats() {
357
/*
P/P * Method: Map getStats()
*
* Preconditions:
* caches != null
*
* Presumptions:
* cache.hits@363 + cache.misses@363 != +0
* java.util.Iterator:next(...)@363 != null
* java.util.Map:values(...)@361 != null
*
* Postconditions:
* return_value == &new HashMap(getStats#1)
* new HashMap(getStats#1) num objects == 1
*
* Test Vectors:
* java.util.Iterator:hasNext(...)@362: {0}, {1}
*/
358 Map allStats = new HashMap();
359
360 Cache cache = null;
361 Iterator cachesIT = caches.values().iterator();
362 while(cachesIT.hasNext()) {
363 cache = (Cache) cachesIT.next();
364
365 allStats.put(cache.getId(), cache.getStats());
366 }
367
368 return allStats;
369 }
370
371
372 /**
373 * Place to do any cleanup tasks for cache system.
374 */
375 public static void shutdown() {
376 // no-op
/*
P/P * Method: void shutdown()
*/
377 }
378
379 }
SofCheck Inspector Build Version : 2.18479
| CacheManager.java |
2009-Jan-02 14:25:42 |
| CacheManager.class |
2009-Sep-04 03:12:32 |