KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > jcorporate > expresso > core > cache > DefaultCacheManager


1 /* ====================================================================
2  * The Jcorporate Apache Style Software License, Version 1.2 05-07-2002
3  *
4  * Copyright (c) 1995-2002 Jcorporate Ltd. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  * notice, this list of conditions and the following disclaimer in
15  * the documentation and/or other materials provided with the
16  * distribution.
17  *
18  * 3. The end-user documentation included with the redistribution,
19  * if any, must include the following acknowledgment:
20  * "This product includes software developed by Jcorporate Ltd.
21  * (http://www.jcorporate.com/)."
22  * Alternately, this acknowledgment may appear in the software itself,
23  * if and wherever such third-party acknowledgments normally appear.
24  *
25  * 4. "Jcorporate" and product names such as "Expresso" must
26  * not be used to endorse or promote products derived from this
27  * software without prior written permission. For written permission,
28  * please contact info@jcorporate.com.
29  *
30  * 5. Products derived from this software may not be called "Expresso",
31  * or other Jcorporate product names; nor may "Expresso" or other
32  * Jcorporate product names appear in their name, without prior
33  * written permission of Jcorporate Ltd.
34  *
35  * 6. No product derived from this software may compete in the same
36  * market space, i.e. framework, without prior written permission
37  * of Jcorporate Ltd. For written permission, please contact
38  * partners@jcorporate.com.
39  *
40  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
41  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
42  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
43  * DISCLAIMED. IN NO EVENT SHALL JCORPORATE LTD OR ITS CONTRIBUTORS
44  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
45  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
46  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
47  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
48  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
49  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
50  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51  * SUCH DAMAGE.
52  * ====================================================================
53  *
54  * This software consists of voluntary contributions made by many
55  * individuals on behalf of the Jcorporate Ltd. Contributions back
56  * to the project(s) are encouraged when you make modifications.
57  * Please send them to support@jcorporate.com. For more information
58  * on Jcorporate Ltd. and its products, please see
59  * <http://www.jcorporate.com/>.
60  *
61  * Portions of this software are based upon other open source
62  * products and are subject to their respective licenses.
63  */

64 package com.jcorporate.expresso.core.cache;
65
66 import EDU.oswego.cs.dl.util.concurrent.ConcurrentReaderHashMap;
67 import com.jcorporate.expresso.core.misc.ConfigCacheManager;
68 import com.jcorporate.expresso.core.misc.ConfigManager;
69 import com.jcorporate.expresso.core.misc.StringUtil;
70 import com.jcorporate.expresso.kernel.ComponentLifecycle;
71 import com.jcorporate.expresso.kernel.Configuration;
72 import com.jcorporate.expresso.kernel.exception.ConfigurationException;
73 import com.jcorporate.expresso.kernel.util.ClassLocator;
74 import com.jcorporate.expresso.kernel.util.FastStringBuffer;
75 import org.apache.log4j.Logger;
76
77 import java.util.ArrayList JavaDoc;
78 import java.util.HashMap JavaDoc;
79 import java.util.Iterator JavaDoc;
80 import java.util.Map JavaDoc;
81 import java.util.Vector JavaDoc;
82
83 /**
84  * [Currently a placeholder. Will be expanded out soon]
85  *
86  * @author Michael Rimov
87  */

88
89 public class DefaultCacheManager
90         extends com.jcorporate.expresso.kernel.ComponentBase
91         implements ComponentLifecycle, CacheSystem {
92
93
94     /**
95      * The actual storage location of the caches.
96      */

97     private Map cacheInstances;
98
99     /**
100      * Min memory percentage indicates the minimum acceptable
101      * percentage of available memory
102      * When freeMemory drops to this minimum, we start dropping
103      * caches until we either drop them
104      * all or there is more than this amount of memory free again
105      * This is checked whenever new items are added or a new cache
106      * is created, or can be checked by
107      * the "checkMemory" method
108      */

109     private static double minMemoryPercentage = 20.00;
110
111     /* When we start trying to free memory, this variable sets a */
112     /* limit on how many iterations through */
113     /* the cache list we can make before stopping - this avoids */
114     /* excessive attempts to free memory */
115     private static int maxRetries = 20;
116
117     /**
118      * listeners is a HashMap of Cacheable/cache relationships - whenever an
119      * update or a clear is made to the cacheable objects, a cacheEvent is sent here,
120      * and all "listeners" to that event have their own cache updated to reflect the
121      * event.
122      */

123     volatile private static HashMap listeners = null;
124
125     /**
126      * The one and only log4j log class.
127      */

128     private static Logger log = Logger.getLogger(DefaultCacheManager.class);
129
130     /**
131      * The interface to the Cache synching mechanism. Using the &lt;class-handler&gr;
132      * section in expresso-config.xml you can plug in remote caches for your system
133      */

134     private static CacheSyncInterface cacheSync = null;
135
136     /**
137      * By default we don't do any cache synchronization.
138      */

139     private static boolean syncInitialized = false;
140
141     public DefaultCacheManager() {
142     }
143
144     /**
145      * Return an iterator over a list of Strings that contain all the names of
146      * the caches stored in the system [For dbContext default]
147      *
148      * @return java.util.Iterator
149      */

150     public java.util.Set JavaDoc getAllCacheNames() {
151         HashMap thisCache = null;
152
153         //Make a copy so iterator points to a snapshot.
154
thisCache = new HashMap(cacheInstances);
155         return thisCache.keySet();
156     }
157
158     /**
159      * Get a particular item in the cache
160      *
161      * @param cacheName The name of the cache
162      * @param valueKey The particular item within the cache to get
163      * @return a Cacheable object or null if it doesn't exist in the cache
164      */

165     public Cacheable getItem(String JavaDoc cacheName, String JavaDoc valueKey) {
166         Cacheable returnValue;
167         Cache thisCache = null;
168
169         thisCache = (Cache) cacheInstances.get(cacheName);
170
171         if (thisCache == null) {
172             if (log.isDebugEnabled()) {
173                 log.debug("Cache " + cacheName + " did not exist ");
174             }
175             returnValue = null;
176         } else {
177             Cacheable co = thisCache.getItem(valueKey);
178             returnValue = co;
179             if (log.isDebugEnabled()) {
180                 if (co == null) {
181                     log.debug("Cache " + thisCache.getName() +
182                             " returns null for key " + valueKey);
183
184                 } else {
185                     log.debug("A " + ((Object JavaDoc) co).getClass().getName() +
186                             " was returned for " + thisCache.getName() +
187                             " key " + valueKey);
188                 }
189             }
190
191
192         }
193
194         return returnValue;
195     }
196
197     /**
198      * Return a count of the number of items in a cache. Return 0 if there is
199      * no such item
200      *
201      * @param cacheName The name of the cache
202      * @return an item count or zero if the cache doesn't exist or is empty;
203      */

204     public int getItemCount(String JavaDoc cacheName) {
205         java.util.List JavaDoc v = getItems(cacheName);
206
207         if (v == null) {
208             return 0;
209         }
210
211         return v.size();
212     }
213
214     /**
215      * Sets a cache to have the particular items specified in itemList.
216      *
217      * @param cacheName The name of the cache
218      * @param itemList The items to set into the cache
219      * @throws CacheException if there's an error setting the items.
220      */

221     public void setItems(String JavaDoc cacheName, java.util.List JavaDoc itemList) throws CacheException {
222         setItems(cacheName, itemList, -1);
223     }
224
225
226     /**
227      * Sets a cache to have the particular items specified in itemList.
228      *
229      * @param cacheName The name of the cache
230      * @param itemList The items to set into the cache
231      * @param expiration the expiration time in milliseconds for the items.
232      * @throws CacheException if there's an error setting the items.
233      */

234     public void setItems(String JavaDoc cacheName, java.util.List JavaDoc itemList, long expiration) throws CacheException {
235         Cache thisCache = null;
236         if (!existsCache(cacheName)) {
237             createCache(cacheName, true);
238         }
239
240         thisCache = this.getCache(cacheName, false);
241         //
242
//thisCache may be null since it may have been deleted by another
243
//thread by the time we get here. Not to worry, we just don't
244
//set the items.
245
//
246
if (thisCache != null) {
247             thisCache.setItems(itemList);
248
249             CacheEntry ce = thisCache.getCacheEntry(cacheName);
250             if (ce != null) {
251                 ce.setExpiration(expiration);
252             }
253
254             notifyRemote(cacheName, "all");
255             notifyListeners(cacheName);
256             checkMemory();
257         }
258     }
259
260
261     /**
262      * Return all of the items in a cache. If the cache was created as an
263      * ordered cache, the items will be in the order they were added. If not,
264      * they will be in no particular order. If there is no such cache or no
265      * items, null will be returned.
266      *
267      * @param cacheName The name of the cache to retrieve
268      * @return java.util.List of Cacheable items
269      */

270     public java.util.List JavaDoc getItems(String JavaDoc cacheName) {
271
272         Vector JavaDoc returnValue = null;
273         Cache thisCache = null;
274
275         if (cacheInstances == null) {
276             returnValue = null;
277         } else {
278             thisCache = (Cache) cacheInstances.get(cacheName);
279
280             if (thisCache == null) {
281                 returnValue = null;
282             } else {
283                 Vector JavaDoc v = thisCache.getItems();
284
285                 if (v == null || v.size() == 0) {
286                     returnValue = null;
287                 } else {
288                     returnValue = v;
289                 }
290             }
291         }
292
293
294         return returnValue;
295     }
296
297     /**
298      * Adds a <code>Cacheable</code> item into the cache
299      *
300      * @param cacheName The name of the cache.
301      * @param newItem The new item to add to the cache
302      * @throws CacheException upon error inserting into the system
303      */

304     public void addItem(String JavaDoc cacheName, Cacheable newItem)
305             throws CacheException {
306         addItem(cacheName, newItem, -1);
307     }
308
309
310     /**
311      * Adds an item to the cache named by parameter cacheName
312      *
313      * @param cacheName The name of the cache to store the object in
314      * @param newItem The new item to add to the cache
315      * @param expiry The time in miliseconds that this cache item will expire
316      * @throws CacheException if there's an error inserting the item into the
317      * cache
318      */

319     public void addItem(String JavaDoc cacheName, Cacheable newItem, long expiry)
320             throws CacheException {
321
322         Cache targetCache = this.getCache(cacheName, true);
323
324         if (targetCache != null) {
325             CacheEntry ce = new CacheEntry(newItem, expiry);
326             targetCache.addItem(ce);
327             notifyListeners(cacheName);
328         }
329
330     }
331
332     /**
333      * Adds a <code>Cacheable</code> item into the cache <em>without</em> clearing
334      * related caches. This is to differentiate between 'changed' items that
335      * are added to the cache via <code>addItem</code> that would require related
336      * caches to be cleared to maintain data integrity.
337      *
338      * @param cacheName the name of the cache to add to
339      * @param newItem the item to add
340      * @throws CacheException upon error putting the item into the cache
341      */

342     public void put(String JavaDoc cacheName, Cacheable newItem)
343             throws CacheException {
344         put(cacheName, newItem, -1);
345     }
346
347     /**
348      * Adds a <code>Cacheable</code> item into the cache <em>without</em> clearing
349      * related caches. This is to differentiate between 'changed' items that
350      * are added to the cache via <code>addItem</code> that would require related
351      * caches to be cleared to maintain data integrity.
352      *
353      * @param cacheName the name of the cache to add to
354      * @param newItem the item to add
355      * @param expiry The time in miliseconds that this cache item will expire
356      * @throws CacheException upon error putting the item into the cache
357      */

358     public void put(String JavaDoc cacheName, Cacheable newItem, long expiry)
359             throws CacheException {
360
361         Cache targetCache = this.getCache(cacheName, true);
362
363         if (targetCache != null) {
364             CacheEntry ce = new CacheEntry(newItem, expiry);
365             targetCache.addItem(ce);
366         }
367     }
368
369
370     /**
371      * Specify a relationship between caches. Whenever an add clear or remove
372      * event is sent to the specified cache, the listener is cleared as well.
373      * Adding a listener implies the relationship between the caches for the
374      * current
375      * db context.
376      *
377      * @param listener The classname of the listener
378      * @param listenTo The name of the cache to listen to.
379      */

380     public void addListener(String JavaDoc listener, String JavaDoc listenTo) {
381         if (listeners == null) {
382             listeners = new HashMap();
383         }
384
385         ArrayList JavaDoc listenerList = (ArrayList JavaDoc) listeners.get(listenTo);
386
387         if (listenerList == null) {
388             listenerList = new ArrayList JavaDoc();
389             listeners.put(listenTo, listenerList);
390         }
391
392         synchronized (listenerList) {
393             if (!listenerList.contains(listener)) {
394                 listenerList.add(listener);
395             }
396         }
397
398     }
399
400     /**
401      * Instructs the cache system to adjust it's usage profile based upon
402      * current memory information that the expresso system is telling us.
403      */

404     public void adjustForMemory() {
405         checkMemory();
406     }
407
408
409     /**
410      * Clear's the named cache.
411      *
412      * @param cacheName The name of the cache to clear
413      * @throws CacheException if there's an error clearing the cache.
414      */

415     public void clear(String JavaDoc cacheName) throws CacheException {
416         notifyRemote(cacheName, "all");
417         clearNoNotify(cacheName);
418         notifyListeners(cacheName);
419     }
420
421     /**
422      * Removes all cache items for a particular data context
423      *
424      * @throws CacheException CacheException if there's an error clearing the
425      * cache
426      */

427     public void clear() throws CacheException {
428         ArrayList JavaDoc cachesToClear = new ArrayList JavaDoc();
429         //Clear out all the valid values
430
for (Iterator JavaDoc it = cacheInstances.values().iterator();
431              it.hasNext();) {
432             Cache thisCache = (Cache) it.next();
433             //We CAN have null values due to multi-thread usage
434
if (thisCache != null) {
435                 cachesToClear.add(thisCache);
436             }
437         }
438
439         //Clear out the individual caches
440
for (Iterator JavaDoc ci = cachesToClear.iterator(); ci.hasNext();) {
441             Cache thisCache = (Cache) ci.next();
442             clear(thisCache.getName()); //uses it's own write lock
443
}
444
445     }
446
447     /**
448      * Clears all caches in this db context but doesn't notify any listeners
449      */

450     public void clearNoNotify() {
451         cacheInstances = new ConcurrentReaderHashMap(30);
452     }
453
454     /**
455      * Clear the named cache, but don't send the remote system notifications.
456      * This method actually removes the cache from the list of available
457      * caches
458      *
459      * @param cacheName The name of the cache
460      */

461     public void clearNoNotify(String JavaDoc cacheName) {
462         Cache thisCache = (Cache) cacheInstances.get(cacheName);
463         if (thisCache != null) {
464             thisCache.clear();
465             cacheInstances.remove(cacheName);
466         }
467     }
468
469     /**
470      * Creates a cache as specified by the parameters listed. Creation date:
471      * (9/7/00 2:18:09 PM)
472      *
473      * @param cacheName java.lang.String the name of the cache
474      * @param ordered boolean true if you want an ordered cache such as for
475      * ValidValues
476      * @param maxSize the maximum size of the cache (zero if boundless)
477      * @return the newly instantiated Cache
478      */

479     public Cache createCache(String JavaDoc cacheName, boolean ordered, int maxSize) throws CacheException {
480         Cache existingCache = this.getCache(cacheName, false);
481         if (existingCache != null) {
482             /* it's already there - never mind! */
483             return existingCache;
484         }
485
486         notifyListeners(cacheName);
487         checkMemory();
488
489         Cache newCache = null;
490         Map thisCacheList = cacheInstances;
491         if (ordered) {
492             String JavaDoc className = ConfigManager.getClassHandler("orderedCache");
493
494             if (className == null || className.length() < 0) {
495                 if (log.isDebugEnabled()) {
496                     log.debug("No class specified for 'orderedCache' class handler. Using default");
497                 }
498             } else {
499                 try {
500                     newCache = (Cache) ClassLocator.loadClass(className).newInstance();
501                 } catch (Exception JavaDoc e) {
502                     log.error("Error loading ordered cache " + className, e);
503                 }
504             }
505             if (newCache == null) {
506                 newCache = new OrderedCache();
507             }
508
509             newCache.setName(cacheName);
510             newCache.setMaxSize(maxSize);
511         } else {
512             String JavaDoc className = ConfigManager.getClassHandler("unOrderedCache");
513
514             if (className == null || className.length() < 0) {
515                 if (log.isDebugEnabled()) {
516                     log.debug("No class specified for 'unOrderedCache' class handler. Using default");
517                 }
518             } else {
519                 try {
520                     newCache = (Cache) ClassLocator.loadClass(className).newInstance();
521                 } catch (Exception JavaDoc e) {
522                     log.error("Error loading ordered cache " + className, e);
523                 }
524             }
525             if (newCache == null) {
526                 newCache = new UnOrderedCache();
527             }
528
529             newCache.setName(cacheName);
530             newCache.setMaxSize(maxSize);
531         }
532         thisCacheList.put(cacheName, newCache);
533         return newCache;
534
535     }
536
537     /**
538      * Creates a cache defined by whether the cache is to be ordered, it's name
539      * and it's maximum size. Creation date: (9/7/00 2:18:09 PM)
540      *
541      * @param cacheName java.lang.String The name of the cache
542      * @param ordered boolean True if you wish for an ordered cache.
543      * @return the newly instantiated cache
544      */

545     public Cache createCache(String JavaDoc cacheName, boolean ordered) throws CacheException {
546         return createCache(cacheName, ordered, 0);
547     }
548
549     /**
550      * Checks to see if the cache already exists. One big note about this is
551      * that unless you already have a ReadLock, the cache may or may not exist
552      * once you go to put your data in the cache. Buyer beware.
553      *
554      * @param cacheName The name of the cache
555      * @return true if the named cache already exists in this data context
556      */

557     public boolean existsCache(String JavaDoc cacheName) {
558         if (cacheInstances != null) {
559             return cacheInstances.containsKey(cacheName);
560         } else {
561             return false;
562         }
563     }
564
565     /**
566      * Retrieve a given cache by name.
567      *
568      * @param cacheName the name of the cache to retrieve.
569      * @return a Cache instance or null if the Cache does not exist.
570      * @see com.jcorporate.expresso.core.cache.Cache
571      */

572     public Cache getCache(String JavaDoc cacheName) {
573         return (Cache) cacheInstances.get(cacheName);
574     }
575
576
577     /**
578      * Removes an item from the cache
579      *
580      * @param cacheName The name of the cache
581      * @param itemToRemove the key of the item to remove
582      */

583     public void removeItem(String JavaDoc cacheName, Cacheable itemToRemove)
584             throws CacheException {
585
586         /* make a "clear" call to all remote caches, if any */
587         notifyRemote(cacheName, itemToRemove.getKey());
588         notifyListeners(cacheName);
589         removeItemNoNotify(cacheName, itemToRemove);
590     }
591
592     /**
593      * Removes an item out of the cache without notifying the cache listeners
594      *
595      * @param cacheName The cache name
596      * @param itemToRemove the key in the cache that has been modified
597      * @throws CacheException Upon error removing the item from the cache
598      */

599     public void removeItemNoNotify(String JavaDoc cacheName, Cacheable itemToRemove)
600             throws CacheException {
601         Cache thisCache = this.getCache(cacheName, false); //get Locks itself
602
if (thisCache != null) {
603             thisCache.removeItem(itemToRemove);
604         }
605
606         /* special case: if we just removed an item from the DBObjLimit cache, */
607         /* that means we could have */
608         /* changed the max cache size of a specific db object */
609         /* In order to handle this, clear the cache of the named DB object */
610         /* (if there is one) */
611         if ("com.jcorporate.expresso.services.dbobj.DbObjLimit".equals(cacheName)) {
612             clear(itemToRemove.getKey());
613         }
614     }
615
616     /**
617      * Creates the 'singleton' CacheSync object. The exact instance is determined
618      * by the cacheSynchronizer classhandler parameter
619      *
620      * @throws ClassNotFoundException if unable to find the class defined
621      * @throws IllegalAccessException if the class did not have a public default
622      * construtor
623      * @throws InstantiationException if there was an error loading instantiating
624      * the cache synchronizer
625      */

626     private static synchronized void createCacheSync()
627             throws ClassNotFoundException JavaDoc,
628             IllegalAccessException JavaDoc,
629             InstantiationException JavaDoc {
630         if (cacheSync != null) {
631             return;
632         }
633
634         String JavaDoc s = ConfigManager.getClassHandler("cacheSynchronizer");
635
636         if (s == null || s.length() == 0) {
637
638             //Nothing listed in the class handler section. No ClassSync
639
//needed
640
syncInitialized = true;
641             cacheSync = null;
642
643             return;
644         }
645
646         cacheSync = (CacheSyncInterface) ClassLocator.loadClass(s).newInstance();
647     }
648
649     public void initialize() {
650         cacheInstances = new ConcurrentReaderHashMap(30);
651     }
652
653     public void configure(Configuration newConfig) throws ConfigurationException {
654         try {
655             createCacheSync();
656         } catch (InstantiationException JavaDoc ex) {
657             log.error("Error creating cache synchronizer. Synchronization disabled", ex);
658         } catch (IllegalAccessException JavaDoc ex) {
659             log.error("Error creating cache synchronizer. Synchronization disabled", ex);
660         } catch (ClassNotFoundException JavaDoc ex) {
661             log.error("Error creating cache synchronizer. Synchronization disabled", ex);
662         }
663     }
664
665
666     /**
667      * Notify remote servers that there has been a request to clear a cache - e.g.
668      * that an update was made
669      *
670      * @param cacheName The cache name
671      * @param key the key in the cache that has been modified
672      * @throws CacheException Upon error notifying the remote objects of the
673      * cache write
674      */

675     private static void notifyRemote(String JavaDoc cacheName,
676                                      String JavaDoc key)
677             throws CacheException {
678
679         if (syncInitialized && cacheSync == null) {
680             return; //No synching done.
681
}
682
683
684         try {
685             if (cacheSync == null) {
686                 createCacheSync();
687
688                 if (cacheSync == null) {
689                     return;
690                 }
691             }
692             synchronized (cacheSync) {
693                 CacheSyncMessage message = new CacheSyncMessage();
694                 message.setCacheName(cacheName);
695                 message.setKey(key);
696 // message.setWebContext(null);
697
cacheSync.notifyRemote(message);
698             }
699         } catch (ClassCastException JavaDoc cce) {
700             log.error("Error loading CacheSyncInterface", cce);
701             throw new CacheException("Error instantiating CacheSyncInterface",
702                     cce);
703         } catch (InstantiationException JavaDoc ie) {
704             log.error("Error loading CacheSyncInterface", ie);
705             throw new CacheException("Error instantiating CacheSyncInterface",
706                     ie);
707         } catch (IllegalAccessException JavaDoc iae) {
708             log.error("Error loading CacheSyncInterface", iae);
709             throw new CacheException("Security error instantiating CacheSyncInterface",
710                     iae);
711         } catch (ClassNotFoundException JavaDoc cnfe) {
712             log.error("Error loading CacheSyncInterface", cnfe);
713             throw new CacheException("Couldn't locate CacheSync Interface specified in expresso-config.xml",
714                     cnfe);
715         }
716     } /* notifyRemote(String, String, String) */
717
718
719     public void reconfigure(Configuration newConfig) throws ConfigurationException {
720         /**@todo Implement this com.jcorporate.expresso.kernel.ComponentLifecycle method*/
721         throw new java.lang.UnsupportedOperationException JavaDoc("Method reconfigure() not yet implemented.");
722     }
723
724     public void destroy() {
725         cacheInstances = null;
726     }
727
728
729     /**
730      * Get a Cache as defined by the dataContext and cacheName
731      *
732      * @param cacheName The name of the cache
733      * @param forceCreate set to true if you want the CacheManager to create a cache
734      * if it doesn't exist yet.
735      * @return The Cache for the specified items or null if it doesn't exist
736      */

737     private Cache getCache(String JavaDoc cacheName, boolean forceCreate) {
738         Cache returnValue = null;
739         try {
740             returnValue = (Cache) cacheInstances.get(cacheName);
741             if (returnValue == null && forceCreate) {
742                 returnValue = createCache(cacheName, false);
743                 if (returnValue == null) {
744                     log.warn("Somebody else removed my own cache before I was able to add it.");
745                 }
746             }
747         } catch (CacheException ex) {
748             log.error("Error creating cache: ", ex);
749         } catch (java.util.ConcurrentModificationException JavaDoc cme) {
750             cme.printStackTrace();
751             throw cme;
752         }
753
754         return returnValue;
755     }
756
757
758     /**
759      * Whenever an add clear or remove event is
760      * sent to the specified cache, the listener is cleared as well.
761      *
762      * @param listenTo The name of the cache that has been modified
763      * @throws CacheException if there's an error notifying the listeners
764      */

765     private void notifyListeners(String JavaDoc listenTo)
766             throws CacheException {
767
768         if (listeners == null) {
769             if (log.isDebugEnabled()) {
770                 log.debug("There are no listeners at all");
771             }
772
773             return;
774         }
775
776         ArrayList JavaDoc listenList = (ArrayList JavaDoc) listeners.get(listenTo);
777
778         if (listenList == null) {
779             if (log.isDebugEnabled()) {
780                 log.debug("There are no listeners to cache " + "/" + listenTo);
781             }
782
783             return;
784         }
785
786         //Make a copy so listening can take place on a static list that won't
787
//be modified
788
synchronized (listenList) {
789             listenList = (ArrayList JavaDoc) listenList.clone();
790         }
791
792         String JavaDoc oneListener = null;
793
794         for (Iterator JavaDoc i = listenList.iterator(); i.hasNext();) {
795             oneListener = (String JavaDoc) i.next();
796
797             if (log.isDebugEnabled()) {
798                 log.debug("Clearing cache " + oneListener +
799                         " because it is " + " a listener to " + listenTo);
800             }
801
802             clear(oneListener);
803         }
804     } /* notifyListeners(String, String) */
805
806     /**
807      * <p/>
808      * Checks to make sure that there is enough memory available to use for more
809      * caches. If it there isn't enough free memory, then it attempts to clear
810      * out the least used cache.</p>
811      * <p/>
812      * Creation date: (9/7/00 2:44:05 PM)</p>
813      */

814     public void checkMemory() {
815         Runtime JavaDoc r = Runtime.getRuntime();
816         double avail = r.totalMemory();
817         double free = r.freeMemory();
818         double pfree = ((free / avail) * 100.000);
819         ConfigCacheManager myConfig = null;
820
821
822         myConfig = ConfigManager.getConfig().getCacheManager();
823
824         if (myConfig != null) {
825             String JavaDoc minMemoryString = StringUtil.notNull(myConfig.getMinMemoryPercentage());
826
827             if (!minMemoryString.equals("")) {
828                 minMemoryPercentage = new Double JavaDoc(minMemoryString).doubleValue();
829             }
830
831             String JavaDoc maxRetriesString = StringUtil.notNull(myConfig.getMaxRetries());
832
833             if (!maxRetriesString.equals("")) {
834                 maxRetries = new Integer JavaDoc(maxRetriesString).intValue();
835             }
836         }
837         if (log.isDebugEnabled()) {
838             log.debug("Free memory " + free + ", Available " + avail +
839                     ", Percent free " + pfree + "%");
840         }
841
842         boolean warned = false;
843         int retries = 0;
844
845         while (pfree < minMemoryPercentage) {
846             retries++;
847
848             if (retries > maxRetries) {
849                 FastStringBuffer warnMessage = FastStringBuffer.getInstance();
850                 try {
851                     warnMessage.append("Tried");
852                     warnMessage.append(maxRetries);
853                     warnMessage.append(" times to free memory - still below minimum");
854                     log.warn(warnMessage.toString());
855                 } finally {
856                     warnMessage.release();
857                     warnMessage = null;
858                 }
859
860
861                 return;
862             }
863             if (log.isInfoEnabled()) {
864                 log.info("Memory below specified minimum limit free - " +
865                         "looking for caches to clear");
866             }
867
868             boolean somethingCleared = clearLowestCache();
869
870             if (!somethingCleared) {
871                 avail = r.totalMemory();
872                 free = r.freeMemory();
873                 pfree = ((free / avail) * 100.00);
874
875                 if (!warned) {
876                     warned = true;
877
878                     FastStringBuffer fsb = FastStringBuffer.getInstance();
879                     try {
880                         fsb.append(
881                                 "WARNING: Out of memory & no more caches can be cleared. To fix, change container (Tomcat) startup to have larger -Xms and -Xmx params for java. Avail:");
882                         fsb.append(avail);
883                         fsb.append(", Free:");
884                         fsb.append(free);
885                         fsb.append(", ");
886                         fsb.append(pfree);
887                         fsb.append("% free");
888                         log.warn(fsb.toString());
889                     } finally {
890                         fsb.release();
891                         fsb = null;
892                     }
893                 }
894             }
895
896             if (log.isInfoEnabled()) {
897                 log.info("Finished clearing cache. Now Garbage Collecting");
898             }
899             System.gc();
900             avail = r.totalMemory();
901             free = r.freeMemory();
902             pfree = ((free / avail) * 100.00);
903         }
904     } /* checkMemory() */
905
906
907     /**
908      * Removed the least used Cache
909      *
910      * @return true if something was able to be removed; false otherwise
911      */

912     public boolean clearLowestCache() {
913
914         String JavaDoc oneCacheName = null;
915         Cache lowestCache = null;
916         Cache oneCache = null;
917         long lowestCount = java.lang.Long.MAX_VALUE;
918
919         Iterator JavaDoc cn = getAllCacheNames().iterator();
920
921         if (cn != null) {
922             while (cn.hasNext()) {
923                 oneCacheName = (String JavaDoc) cn.next();
924                 oneCache = getCache(oneCacheName, false);
925
926                 if (oneCache != null &&
927                         (oneCache.getUsedCount() < lowestCount)) {
928                     lowestCache = oneCache;
929                     lowestCount = oneCache.getUsedCount();
930                 }
931             } /* while */
932
933         }
934
935 // }
936

937         if (lowestCache != null) {
938             if (log.isDebugEnabled()) {
939                 log.debug("Cache '" + lowestCache.getName() +
940                         "' is least used & being cleared");
941             }
942
943             lowestCache.clear();
944         } else {
945             return false;
946         }
947
948         return true;
949     }
950
951     /**
952      * Displays the cache status. Currently this is only really used for
953      * debugging purposes.
954      * Creation date: (9/7/00 2:44:05 PM)
955      */

956     public void displayStatus() {
957         Runtime JavaDoc r = Runtime.getRuntime();
958         double avail = r.totalMemory();
959         double free = r.freeMemory();
960         double pfree = ((free / avail) * 100.000);
961
962         if (log.isInfoEnabled()) {
963             log.info("Cache Status");
964             log.info("Free memory " + free + ", Available " + avail +
965                     ", Percent free " + pfree + "%");
966         }
967
968         String JavaDoc oneConfigKey = null;
969         String JavaDoc oneCacheName = null;
970         Cache oneCache = null;
971
972         Iterator JavaDoc cn = this.getAllCacheNames().iterator();
973
974         if (cn != null) {
975             while (cn.hasNext()) {
976                 oneCacheName = (String JavaDoc) cn.next();
977                 oneCache = this.getCache(oneCacheName, false);
978                 if (oneCache != null) {
979                     if (log.isDebugEnabled()) {
980                         log.debug("Context/Cache " + oneConfigKey + "/" +
981                                 oneCacheName + " has " +
982                                 oneCache.getItemCount() + " items & " +
983                                 oneCache.getUsedCount() + " accesses");
984                     }
985                 }
986             } /* while */
987
988         } /* if cn is not null */
989
990     } /* displayStatus() */
991
992
993 }
Popular Tags