001: /* ====================================================================
002: * The Jcorporate Apache Style Software License, Version 1.2 05-07-2002
003: *
004: * Copyright (c) 1995-2002 Jcorporate Ltd. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * 1. Redistributions of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * 2. Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * 3. The end-user documentation included with the redistribution,
019: * if any, must include the following acknowledgment:
020: * "This product includes software developed by Jcorporate Ltd.
021: * (http://www.jcorporate.com/)."
022: * Alternately, this acknowledgment may appear in the software itself,
023: * if and wherever such third-party acknowledgments normally appear.
024: *
025: * 4. "Jcorporate" and product names such as "Expresso" must
026: * not be used to endorse or promote products derived from this
027: * software without prior written permission. For written permission,
028: * please contact info@jcorporate.com.
029: *
030: * 5. Products derived from this software may not be called "Expresso",
031: * or other Jcorporate product names; nor may "Expresso" or other
032: * Jcorporate product names appear in their name, without prior
033: * written permission of Jcorporate Ltd.
034: *
035: * 6. No product derived from this software may compete in the same
036: * market space, i.e. framework, without prior written permission
037: * of Jcorporate Ltd. For written permission, please contact
038: * partners@jcorporate.com.
039: *
040: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
041: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
042: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
043: * DISCLAIMED. IN NO EVENT SHALL JCORPORATE LTD OR ITS CONTRIBUTORS
044: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
045: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
046: * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
047: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
048: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
049: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
050: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
051: * SUCH DAMAGE.
052: * ====================================================================
053: *
054: * This software consists of voluntary contributions made by many
055: * individuals on behalf of the Jcorporate Ltd. Contributions back
056: * to the project(s) are encouraged when you make modifications.
057: * Please send them to support@jcorporate.com. For more information
058: * on Jcorporate Ltd. and its products, please see
059: * <http://www.jcorporate.com/>.
060: *
061: * Portions of this software are based upon other open source
062: * products and are subject to their respective licenses.
063: */
064:
065: package com.jcorporate.expresso.core.cache;
066:
067: import com.jcorporate.expresso.core.misc.ConfigManager;
068: import com.jcorporate.expresso.kernel.ComponentLifecycle;
069: import com.jcorporate.expresso.kernel.management.ExpressoRuntimeMap;
070: import com.jcorporate.expresso.kernel.util.LocatorUtils;
071: import org.apache.commons.collections.FastHashMap;
072: import org.apache.log4j.Logger;
073:
074: import java.util.Enumeration;
075: import java.util.Iterator;
076: import java.util.Vector;
077:
078: /**
079: * CacheManager.java
080: * <p>Copyright 1999-2002 Jcorporate Ltd.</p>
081: * <p/>
082: * Cache Manager is a general-purpose class to handle all different
083: * kinds of caches - it provides both "ordered" caches (stored as a
084: * Vector of objects) and "unordered" caches (stored as a Hashtable).
085: * </p>
086: * <p/>
087: * The amount of data cached can be expressed as a count of items in
088: * the specified list (e.g. cache only so many items) or limited by
089: * the amount of free memory available (e.g. don't go below 10% of
090: * available memory being free). In both cases, the cached items are
091: * removed from the cache according to how frequently they have been
092: * used (e.g. a least-frequently-used algorithm is used to drop items
093: * from the cache).
094: * </p>
095: *
096: * @since Expresso 1.0
097: */
098: public class CacheManager {
099:
100: /* cacheLists is a hashtable keyed by context that contains the hashtables */
101: /* for each context. Each entry in the enclosed hashtable is a Cache */
102: private volatile static FastHashMap cacheLists = null;
103:
104: /**
105: * The one and only instance of this Cache Manager
106: */
107: private static CacheManager theInstance = null;
108:
109: private static String this ContextPath = null;
110:
111: /**
112: * The one and only log4j log class.
113: */
114: private static Logger log = Logger.getLogger(CacheManager.class);
115:
116: /**
117: * Flag to tell quickly whether this was initialized by the new or
118: * old configuration system.
119: */
120: private static boolean runtimeInitialized = false;
121:
122: private static CacheCleaner cacheCleaner;
123:
124: /**
125: * Default Constructor. Should actually only really be called by getInstance(), and
126: * it will eventually become protected
127: */
128: public CacheManager() {
129: super ();
130:
131: runtimeInitialized = (ExpressoRuntimeMap.getDefaultRuntime() != null);
132:
133: if (!runtimeInitialized) {
134: cacheLists = new FastHashMap();
135: this ContextPath = ConfigManager.getWebAppDir();
136: cacheCleaner = new CacheCleaner();
137: cacheLists.setFast(false);
138:
139: for (Enumeration e = ConfigManager.getAllConfigKeys(); e
140: .hasMoreElements();) {
141: String oneContext = (String) e.nextElement();
142: DefaultCacheManager dcm = new DefaultCacheManager();
143: dcm.initialize();
144: cacheLists.put(oneContext, dcm);
145: cacheCleaner.registerCacheSystem(dcm);
146: }
147: cacheLists.setFast(true);
148: cacheCleaner.setDaemon(true);
149: cacheCleaner.setName("Cache Cleaner");
150: cacheCleaner.setPriority(Thread.MIN_PRIORITY);
151: cacheCleaner.start();
152: }
153: } /* CacheManager() */
154:
155: /**
156: * <p/>
157: * Singleton instantiation. First step in migrating away from
158: * static methods.</p>
159: * <p/>
160: * Ideally call this and operate on the instance() instead of the static
161: * member variables.</p>
162: *
163: * @return the instance of the CacheManager
164: */
165: public synchronized static CacheManager getInstance() {
166: if (theInstance == null) {
167: theInstance = new CacheManager();
168: }
169:
170: return theInstance;
171: }
172:
173: /**
174: * Retrieve the cache system appropriate for the data context
175: *
176: * @param dataContext the data context to retrieve the cache system for
177: * @return Cache system or null in the Component system when caching is
178: * not enabled.
179: */
180: public final static CacheSystem getCacheSystem(String dataContext) {
181: if (dataContext == null || dataContext.length() == 0) {
182: dataContext = "default";
183: }
184:
185: if (CacheManager.runtimeInitialized) {
186: LocatorUtils lc = new LocatorUtils(ExpressoRuntimeMap
187: .getDefaultRuntime());
188: return (CacheSystem) lc.locateComponent(dataContext
189: + ".Cache");
190: } else {
191: return (CacheSystem) cacheLists.get(dataContext);
192: }
193: }
194:
195: /**
196: * Destroys the CacheManager. clears all caches, closes all cache sync instances
197: * and all the other miscellaneous cleanup.
198: */
199: public synchronized static void destroy() {
200: if (runtimeInitialized) {
201: //The component system itself takes care of the runtime
202: return;
203: } else {
204: cacheCleaner.interrupt();
205: try {
206: cacheCleaner.join();
207: } catch (InterruptedException ex) {
208: log
209: .info("Interrupted while waiting for cache cleaner to exit");
210: }
211:
212: for (Iterator i = cacheLists.values().iterator(); i
213: .hasNext();) {
214: CacheSystem cs = (CacheSystem) i.next();
215: if (cs instanceof ComponentLifecycle) {
216: ((ComponentLifecycle) cs).destroy();
217: }
218: }
219:
220: cacheLists.clear();
221: }
222:
223: System.gc(); //Clean up the memory that cache manager held.
224: }
225:
226: /**
227: * Adds a <code>Cacheable</code> item into the cache
228: *
229: * @param dataContext the data context that this item is to reside in.
230: * @param cacheName The name of the cache.
231: * @param newItem The new item to add to the cache
232: * @throws CacheException upon error inserting into the system
233: */
234: public static void addItem(String dataContext, String cacheName,
235: Cacheable newItem) throws CacheException {
236: CacheSystem cs = getCacheSystem(dataContext);
237: if (cs == null) {
238: return;
239: } else {
240: cs.addItem(cacheName, newItem);
241: }
242: } /* addItem(String, String, Cacheable) */
243:
244: /**
245: * Adds an item to the cache named by parameter cacheName
246: *
247: * @param dataContext The data context that this cache is for
248: * @param cacheName The name of the cache to store the object in
249: * @param newItem The new item to add to the cache
250: * @param expiry The time in miliseconds that this cache item will expire
251: * @throws CacheException if there's an error inserting the item into the
252: * cache
253: */
254: public static void addItem(String dataContext, String cacheName,
255: Cacheable newItem, long expiry) throws CacheException {
256: CacheSystem cs = getCacheSystem(dataContext);
257: if (cs == null) {
258: return;
259: } else {
260: cs.addItem(cacheName, newItem, expiry);
261: }
262: } /* addItem(String, String, Cacheable, long) */
263:
264: /**
265: * Specify a relationship between caches. Whenever an add clear or
266: * remove event is sent to the specified cache, the listener is
267: * cleared as well. Adding a listener implies the relationship between
268: * the caches for ALL db contexts.
269: *
270: * @param listener The classname of the listener
271: * @param listenTo The name of the cache to listen to.
272: */
273: public static void addListener(String listener, String listenTo) {
274:
275: for (Enumeration e = ConfigManager.getAllConfigKeys(); e
276: .hasMoreElements();) {
277: String oneContext = (String) e.nextElement();
278: CacheSystem cs = getCacheSystem(oneContext);
279: if (cs == null) {
280: continue;
281: } else {
282: cs.addListener(listener, listenTo);
283: }
284: }
285: } /* addListener(String, String) */
286:
287: /**
288: * Removes all cache items for a particular data context
289: *
290: * @param dataContext The data context to clear all items for.
291: * @throws CacheException CacheException if there's an error clearing the
292: * cache
293: */
294: public static void clear(String dataContext) throws CacheException {
295: CacheSystem cs = getCacheSystem(dataContext);
296: if (cs == null) {
297: log.warn("Unable to locate cache system for data context "
298: + dataContext);
299: return;
300: } else {
301: cs.clear();
302: }
303: } /* clear(String) */
304:
305: /**
306: * Clears all caches in this db context but doesn't notify any listeners
307: *
308: * @param dataContext The data context that this cache is mapped to
309: */
310: public static void clearNoNotify(String dataContext) {
311: CacheSystem cs = getCacheSystem(dataContext);
312: if (cs == null) {
313: log.warn("Unable to locate cache system for data context "
314: + dataContext);
315: return;
316: } else {
317: cs.clearNoNotify();
318: }
319: } /* clearNoNotify(String) */
320:
321: /**
322: * Clear's the named cache.
323: *
324: * @param dataContext The dbContext of the cache
325: * @param cacheName The name of the cache to clear
326: * @throws CacheException if there's an error clearing the cache.
327: */
328: public static void clear(String dataContext, String cacheName)
329: throws CacheException {
330: CacheSystem cs = getCacheSystem(dataContext);
331: if (cs == null) {
332: log.warn("Unable to locate cache system for data context "
333: + dataContext);
334: return;
335: } else {
336: cs.clear(cacheName);
337: }
338: } /* clear(String, String) */
339:
340: /**
341: * Clear the named cache, but don't send the remote system notifications.
342: * This method actually removes the cache from the list of available caches
343: *
344: * @param dataContext The dbContext that the cache is attached to
345: * @param cacheName The name of the cache
346: */
347: public static void clearNoNotify(String dataContext,
348: String cacheName) {
349: CacheSystem cs = getCacheSystem(dataContext);
350: if (cs == null) {
351: return;
352: } else {
353: cs.clearNoNotify(cacheName);
354: }
355: } /* clearNoNotify(String, String) */
356:
357: /**
358: * Creates a cache as specified by the parameters listed.
359: * Creation date: (9/7/00 2:18:09 PM)
360: *
361: * @param dataContext java.lang.String The data context for your app
362: * @param cacheName java.lang.String the name of the cache
363: * @param ordered boolean true if you want an ordered cache such as for
364: * ValidValues
365: * @return the newly instantiated Cache
366: */
367: public static Cache createCache(String dataContext,
368: String cacheName, boolean ordered) throws CacheException {
369: CacheSystem cs = getCacheSystem(dataContext);
370: if (cs == null) {
371: log.warn("Unable to locate cache system for data context "
372: + dataContext);
373: return null;
374: } else {
375: return cs.createCache(cacheName, ordered);
376: }
377:
378: } /* createCache(String, String, boolean) */
379:
380: /**
381: * Creates a cache defined by whether the cache is to be ordered, it's name
382: * and it's maximum size.
383: * Creation date: (9/7/00 2:18:09 PM)
384: *
385: * @param dataContext java.lang.String The dbcontext that this cache is for
386: * @param cacheName java.lang.String The name of the cache
387: * @param ordered boolean True if you wish for an ordered cache.
388: * @param maxSize The maximum size of the cache
389: * @return the newly instantiated cache
390: */
391: public static Cache createCache(String dataContext,
392: String cacheName, boolean ordered, int maxSize)
393: throws CacheException {
394:
395: CacheSystem cs = getCacheSystem(dataContext);
396: if (cs == null) {
397: log.warn("Unable to locate cache system for data context "
398: + dataContext);
399: return null;
400: } else {
401: return cs.createCache(cacheName, ordered, maxSize);
402: }
403:
404: } /* createCache(String, String, boolean, int) */
405:
406: /**
407: * Displays the cache status. Currently this is only really used for
408: * debugging purposes.
409: * Creation date: (9/7/00 2:44:05 PM)
410: */
411: public static void displayStatus() {
412: Runtime r = Runtime.getRuntime();
413: double avail = r.totalMemory();
414: double free = r.freeMemory();
415: double pfree = ((free / avail) * 100.000);
416:
417: if (log.isInfoEnabled()) {
418: log.info("Cache Status");
419: log.info("Free memory " + free + ", Available " + avail
420: + ", Percent free " + pfree + "%");
421: }
422:
423: String oneConfigKey = null;
424:
425: for (Enumeration e = ConfigManager.getAllConfigKeys(); e
426: .hasMoreElements();) {
427: oneConfigKey = (String) e.nextElement();
428: CacheSystem cs = CacheManager.getCacheSystem(oneConfigKey);
429: if (cs != null) {
430: cs.displayStatus();
431: } else {
432: continue;
433: }
434: // ((DefaultCacheManager)getCacheSystem(oneConfigKey)).displayStatus();
435: } /* for each config key */
436:
437: } /* displayStatus() */
438:
439: /**
440: * Checks to see if the cache already exists. One big note about this is
441: * that unless you already have a ReadLock, the cache may or may not exist
442: * once you go to put your data in the cache. Buyer beware.
443: *
444: * @param dataContext The dbContext that this cache is hooked to
445: * @param cacheName The name of the cache
446: * @return true if the named cache already exists in this data context
447: */
448: public static boolean existsCache(String dataContext,
449: String cacheName) {
450: CacheSystem cs = getCacheSystem(dataContext);
451: if (cs == null) {
452: return false;
453: } else {
454: return cs.existsCache(cacheName);
455: }
456: // return getCacheSystem(dataContext).existsCache(cacheName);
457: } /* existsCache(String, String) */
458:
459: /**
460: * Get the names of all of the Caches being held for a particular context
461: *
462: * @param dataContext The dbContext to get the cache names for.
463: * @return java.util.Iterator
464: */
465: public static Iterator getAllCacheNamesIterator(String dataContext) {
466: CacheSystem cs = getCacheSystem(dataContext);
467: if (cs == null) {
468: return new java.util.ArrayList(0).iterator();
469: } else {
470: return cs.getAllCacheNames().iterator();
471: }
472: } /* getAllCacheNames(String) */
473:
474: /**
475: * Get a particular item in the cache
476: *
477: * @param dataContext The dbContext for this cache
478: * @param cacheName The name of the cache
479: * @param valueKey The particular item within the cache to get
480: * @return a Cacheable object or null if it doesn't exist in the cache
481: */
482: public static Cacheable getItem(String dataContext,
483: String cacheName, String valueKey) {
484: CacheSystem cs = getCacheSystem(dataContext);
485: if (cs == null) {
486: return null;
487: } else {
488: return cs.getItem(cacheName, valueKey);
489: }
490:
491: // return getCacheSystem(dataContext).getItem(cacheName,valueKey);
492: } /* getItem(String, String, String) */
493:
494: /**
495: * Return all of the items in a cache. If the cache was created as an ordered
496: * cache, the items will be in the order they were added. If not, they will be
497: * in no particular order.
498: * If there is no such cache or no items, null will be returned.
499: *
500: * @param dataContext The dbcontext for this cache
501: * @param cacheName The name of the cache to retrieve
502: * @return java.util.Vector of Cacheable items
503: */
504: public static Vector getItems(String dataContext, String cacheName) {
505: CacheSystem cs = getCacheSystem(dataContext);
506: if (cs == null) {
507: return null;
508: } else {
509: java.util.List l = getCacheSystem(dataContext).getItems(
510: cacheName);
511: if (l == null) {
512: return null;
513: } else {
514: if (l instanceof java.util.Vector) {
515: return (Vector) l;
516: } else {
517: return new Vector(l);
518: }
519: }
520: }
521:
522: // java.util.List l = getCacheSystem(dataContext).getItems(cacheName);
523: // if (l == null) {
524: // return null;
525: // } else {
526: // return new Vector(l);
527: // }
528: // return new Vector(getCacheSystem(dataContext).getItems(cacheName));
529: } /* getItems(String, String) */
530:
531: /**
532: * Return a count of the number of items in a cache. Return 0 if there is
533: * no such item
534: *
535: * @param dataContext the dbContext to retrieve from.
536: * @param cacheName The name of the cache
537: * @return an item count or zero if the cache doesn't exist or is empty;
538: */
539: public static int getItemCount(String dataContext, String cacheName) {
540: CacheSystem cs = getCacheSystem(dataContext);
541: if (cs == null) {
542: return 0;
543: } else {
544: return cs.getItemCount(cacheName);
545: }
546:
547: // return getCacheSystem(dataContext).getItemCount(cacheName);
548: } /* getItemCount(String, String) */
549:
550: /**
551: * Removes an item from the cache
552: *
553: * @param dataContext The data context for the cache
554: * @param cacheName The name of the cache
555: * @param itemToRemove the key of the item to remove
556: */
557: public static void removeItem(String dataContext, String cacheName,
558: Cacheable itemToRemove) throws CacheException {
559: CacheSystem cs = getCacheSystem(dataContext);
560: if (cs == null) {
561: return;
562: } else {
563: cs.removeItem(cacheName, itemToRemove);
564: }
565: // getCacheSystem(dataContext).removeItem(cacheName,itemToRemove);
566:
567: } /* removeItem(String, String, Cacheable) */
568:
569: /**
570: * Removes an item out of the cache without notifying the cache listeners
571: *
572: * @param dataContext The name of the data context
573: * @param cacheName The cache name
574: * @param itemToRemove the key in the cache that has been modified
575: * @throws CacheException Upon error removing the item from the cache
576: */
577: public static void removeItemNoNotify(String dataContext,
578: String cacheName, Cacheable itemToRemove)
579: throws CacheException {
580: CacheSystem cs = getCacheSystem(dataContext);
581: if (cs == null) {
582: return;
583: } else {
584: cs.removeItemNoNotify(cacheName, itemToRemove);
585: }
586:
587: // getCacheSystem(dataContext).removeItemNoNotify(cacheName,itemToRemove);
588: } /* removeItemNoNotify(String, String, Cacheable) */
589:
590: /**
591: * Sets a cache to have the particular items specified in itemList.
592: *
593: * @param dataContext The dbContext for the cache
594: * @param cacheName The name of the cache
595: * @param itemList The items to set into the cache
596: * @throws CacheException if there's an error setting the items.
597: */
598: public static void setItems(String dataContext, String cacheName,
599: Vector itemList) throws CacheException {
600: CacheSystem cs = getCacheSystem(dataContext);
601: if (cs == null) {
602: return;
603: } else {
604: cs.setItems(cacheName, itemList);
605: }
606:
607: // getCacheSystem(dataContext).setItems(cacheName,itemList);
608: } /* setItems(String, String, Vector) */
609: }
610:
611: /* CacheManager */
|