KickJava   Java API By Example, From Geeks To Geeks.

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


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
65 package com.jcorporate.expresso.core.cache;
66
67 import EDU.oswego.cs.dl.util.concurrent.ReadWriteLock;
68 import EDU.oswego.cs.dl.util.concurrent.WriterPreferenceReadWriteLock;
69 import org.apache.commons.collections.LRUMap;
70 import org.apache.log4j.Logger;
71
72 import java.util.HashMap JavaDoc;
73 import java.util.Iterator JavaDoc;
74 import java.util.Map JavaDoc;
75 import java.util.Vector JavaDoc;
76
77
78 /**
79  * Unordered cache is a hash map backed class. As such, access to individual items
80  * is relatively quick. However getting all the items in cache is a very slow process,
81  * and the order of the items is not guaranteed.
82  * <p/>
83  * If the size of the unordered cache is specified, then the cache automatically uses
84  * the least recently used (LRU) algorithm to determine which items should be removed
85  * from the cache.
86  * <p/>
87  * Unless you need to specifically store arrays of objects then this will be the cache
88  * to use for most of your needs.
89  * Creation date: (9/7/00 11:47:14 AM)
90  */

91 public class UnOrderedCache
92         implements Cache {
93
94     /**
95      * Read Write lock on the cache contents so we can concurrently read from the
96      * cache.
97      */

98     private final ReadWriteLock cacheLock = new WriterPreferenceReadWriteLock();
99
100     /**
101      * Access count for this cache
102      */

103     private long accessCount = 0;
104
105     //The actual cache contents. Access must be synchronized.
106
//Default is a hashmap, but if maxsize > 0 , then LRU map is
107
//used instead
108
private Map cacheContents = java.util.Collections.synchronizedMap(new HashMap JavaDoc());
109
110     //
111
private String JavaDoc cacheName = null;
112
113     //This class
114
private static final String JavaDoc thisClass = UnOrderedCache.class.getName() + ".";
115
116     //Maxium size
117
private int maxSize = 0;
118
119     //The log
120
private static final transient Logger log = Logger.getLogger(UnOrderedCache.class);
121
122     /**
123      * UnOrderedCache constructor comment.
124      */

125     public UnOrderedCache() {
126         super();
127     } /* UnOrderedCache() */
128
129     private final synchronized void incrementAccessCount() {
130         accessCount++;
131     }
132
133     /**
134      * Adds an item to this cache
135      *
136      * @param newItem The new item to cache
137      */

138     public void addItem(CacheEntry newItem) {
139         incrementAccessCount();
140
141         if (log.isDebugEnabled()) {
142             log.debug("Cache " + getName() + " Added item " +
143                     newItem.getKey());
144         }
145
146         try {
147             cacheLock.writeLock().acquire();
148         } catch (InterruptedException JavaDoc ex) {
149             log.error("Interrupted waiting for write lock. Aborting method", ex);
150             return;
151         }
152
153         try {
154             //Simply put it in. LRUMap if maxsize is needed, takes care
155
//of removing the least recently used items.
156
cacheContents.put(newItem.getKey(), newItem);
157
158             newItem.clearUsedCount();
159         } finally {
160             cacheLock.writeLock().release();
161         }
162     } /* addItem(CacheEntry) */
163
164     /**
165      * Clear the cache by creating a new map
166      */

167     public void clear() {
168         try {
169             cacheLock.writeLock().acquire();
170         } catch (InterruptedException JavaDoc ex) {
171             log.error("Interrupted waiting for write lock. Aborting method", ex);
172             return;
173         }
174         try {
175             if (maxSize > 0) {
176                 cacheContents = java.util.Collections.synchronizedMap(new LRUMap(maxSize));
177             } else {
178                 cacheContents = java.util.Collections.synchronizedMap(new HashMap JavaDoc());
179             }
180         } finally {
181             cacheLock.writeLock().release();
182         }
183     } /* clear() */
184
185     /**
186      * Retrieve the cache entry specified by the item key
187      *
188      * @param itemKey the key for the cache entry
189      * @return the resulting cache entry or null if it no longer exists.
190      */

191     public CacheEntry getCacheEntry(String JavaDoc itemKey) {
192         incrementAccessCount();
193         CacheEntry returnValue = null;
194         boolean removeCacheItem = false;
195         CacheEntry oneItem = null;
196         try {
197             cacheLock.readLock().acquire();
198         } catch (InterruptedException JavaDoc ex) {
199             log.error("Interrupted waiting for write lock. Aborting method", ex);
200             return null;
201         }
202
203         try {
204             oneItem = (CacheEntry) cacheContents.get(itemKey);
205
206             if (oneItem != null) {
207                 if (oneItem.isExpired()) {
208                     if (log.isDebugEnabled()) {
209                         log.debug("Item '" + itemKey +
210                                 "' expired & was removed. Expiry was " +
211                                 oneItem.getExpires());
212                     }
213                     removeCacheItem = true;
214
215                     //Don't set the return value
216
} else {
217                     //We're ok here..
218
oneItem.incrementUsedCount();
219                     returnValue = oneItem;
220                 }
221             }
222
223         } finally {
224             cacheLock.readLock().release();
225         }
226
227         //We have to remove here after the readlock is released, otherwise
228
//we cause deadlock
229
if (removeCacheItem) {
230             removeItem(oneItem.getContents());
231             return null;
232         }
233
234         return returnValue;
235     }
236
237     /**
238      * Get an item as defined by the key.
239      *
240      * @param itemKey The item's key
241      * @return <code>Cacheable</code> object or null if it doesn't exist in the
242      * cache
243      */

244     public Cacheable getItem(String JavaDoc itemKey) {
245         //Locking isn't necessary since we get the cache entry through an embedded
246
//lock
247
CacheEntry oneItem = this.getCacheEntry(itemKey);
248         if (oneItem == null) {
249             return null;
250         }
251
252         if (oneItem.getContents() == null) {
253             if (log.isDebugEnabled()) {
254                 log.debug("Item '" + itemKey + "' had null contents");
255             }
256         }
257
258         return oneItem.getContents();
259     } /* getItem(String) */
260
261     /**
262      * Get the number of items in the cache
263      *
264      * @return the number of items in this cache
265      */

266     public int getItemCount() {
267         int returnvalue = 0;
268         try {
269             cacheLock.readLock().acquire();
270         } catch (InterruptedException JavaDoc ex) {
271             log.error("Interrupted waiting for write lock. Aborting method", ex);
272             return 0;
273         }
274         try {
275             returnvalue = cacheContents.size();
276         } finally {
277             cacheLock.readLock().release();
278         }
279         return returnvalue;
280     } /* getItemCount() */
281
282     /**
283      * Return all the items in a Vector This is a REALLY messy function
284      * for unordered caches. Use very INFREQUENTLY
285      *
286      * @return <code>java.util.Vector</code> of all the items.
287      */

288     public Vector JavaDoc getItems() {
289         incrementAccessCount();
290         Vector JavaDoc v = null;
291         try {
292             cacheLock.writeLock().acquire();
293         } catch (InterruptedException JavaDoc ex) {
294             log.error("Interrupted waiting for write lock. Aborting method", ex);
295             return null;
296         }
297
298         try {
299             if (cacheContents.size() <= 0) {
300                 return null;
301             }
302
303             //Kind of pre-allocate to reduce time in the syncrhonized block.
304
//It's quite possible that final v will be different size then lenght,
305
//but since it's dynamic it should be able to handle that no problem.
306
int length = cacheContents.size();
307             v = new Vector JavaDoc(length);
308
309             for (Iterator JavaDoc i = cacheContents.values().iterator(); i.hasNext();) {
310                 CacheEntry oneItem = (CacheEntry) i.next();
311                 if (oneItem.isExpired()) {
312                     i.remove();
313                 } else {
314                     oneItem.incrementUsedCount();
315                     v.addElement(oneItem.getContents());
316                 }
317             }
318         } finally {
319             cacheLock.writeLock().release();
320         }
321
322         return v;
323     } /* getItems() */
324
325     /**
326      * Returns the name of this cache
327      *
328      * @return <code>java.lang.String</code> the name of the cache
329      */

330     public synchronized String JavaDoc getName() {
331         return cacheName;
332     } /* getName() */
333
334     /**
335      * Return the number of times this cache has been accessed
336      *
337      * @return the number of times accessed as integer
338      */

339     public synchronized long getUsedCount() {
340         return accessCount;
341     } /* getUsedCount() */
342
343     /**
344      * Retrieve whether the cache instance is an ordered cache [list based]
345      * or unordered cache. [map based]
346      *
347      * @return true if the cache is an ordered cache.
348      */

349     public boolean isOrdered() {
350         return false;
351     }
352
353
354     /**
355      * Removes an item as keyed by the paramter
356      *
357      * @param oldItem The item to remove.
358      */

359     public synchronized void removeItem(Cacheable oldItem) {
360         try {
361             cacheLock.writeLock().acquire();
362         } catch (InterruptedException JavaDoc ex) {
363             log.error("Interrupted waiting for write lock. Aborting method", ex);
364             return;
365         }
366         try {
367             cacheContents.remove(oldItem.getKey());
368         } finally {
369             cacheLock.writeLock().release();
370         }
371     } /* removeItem(Cacheable) */
372
373
374     /**
375      * Eventually the new way to set items
376      *
377      * @param newItems the new items to add
378      * @throws CacheException upon error
379      */

380     public void setItems(java.util.List JavaDoc newItems) throws CacheException {
381         if (newItems instanceof java.util.Vector JavaDoc) {
382             setItems(newItems);
383         } else {
384             setItems(new Vector JavaDoc(newItems));
385         }
386     }
387
388     /**
389      * Goes through all the new items and adds them to the list.
390      *
391      * @param newItems A <code>java.util.Vector</code> of new items to add to
392      * the cache list
393      * @throws CacheException if there's an error adding the cache entry items
394      */

395     public void setItems(Vector JavaDoc newItems)
396             throws CacheException {
397         incrementAccessCount();
398         clear();
399         try {
400             cacheLock.writeLock().acquire();
401         } catch (InterruptedException JavaDoc ex) {
402             log.error("Interrupted waiting for write lock. Aborting method", ex);
403             return;
404         }
405
406         try {
407             Object JavaDoc oneObject = null;
408             int size = newItems.size();
409             for (int i = 0; i < size; i++) {
410                 oneObject = newItems.elementAt(i);
411
412                 if (oneObject instanceof Cacheable) {
413                     CacheEntry ce = new CacheEntry((Cacheable) oneObject, -1);
414                     addItem(ce);
415                 } else {
416                     String JavaDoc myName = thisClass + ":setItems()";
417                     throw new CacheException(myName +
418                             ":Item in vector is of class " +
419                             oneObject.getClass().getName() +
420                             ", which is not Cacheable - cannot set items");
421                 }
422             }
423         } finally {
424             cacheLock.writeLock().release();
425         }
426     } /* setItems(Vector) */
427
428
429     /**
430      * Sets the maximum size for the cache. If it is greater than zero then
431      * we use an LRU Map instead of a normal hashmap to provide automatic
432      * removal of old items.
433      *
434      * @param newMaxSize The new maximum size to use for the cache
435      */

436     public synchronized void setMaxSize(int newMaxSize) {
437         try {
438             cacheLock.writeLock().acquire();
439         } catch (InterruptedException JavaDoc ex) {
440             log.error("Interrupted waiting for write lock. Aborting method", ex);
441             return;
442         }
443
444         try {
445             if (newMaxSize > 0) {
446                 LRUMap newMap = new LRUMap(newMaxSize);
447                 if (cacheContents.size() > 0) {
448                     newMap.putAll(cacheContents);
449                 }
450                 cacheContents = java.util.Collections.synchronizedMap(newMap);
451             } else {
452                 cacheContents = java.util.Collections.synchronizedMap(new HashMap JavaDoc(cacheContents));
453             }
454
455             maxSize = newMaxSize;
456         } finally {
457             cacheLock.writeLock().release();
458         }
459     } /* setMaxSize(int) */
460
461     /**
462      * Sets the name of the cache
463      *
464      * @param newName the new name to set for the cache.
465      */

466     public synchronized void setName(String JavaDoc newName) {
467         cacheName = newName;
468     } /* setName(String) */
469
470 } /* UnOrderedCache */
471
Popular Tags