KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > ejb > containers > util > cache > LruCache


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the License). You may not use this file except in
5  * compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * https://glassfish.dev.java.net/public/CDDLv1.0.html or
9  * glassfish/bootstrap/legal/CDDLv1.0.txt.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * Header Notice in each file and include the License file
15  * at glassfish/bootstrap/legal/CDDLv1.0.txt.
16  * If applicable, add the following below the CDDL Header,
17  * with the fields enclosed by brackets [] replaced by
18  * you own identifying information:
19  * "Portions Copyrighted [year] [name of copyright owner]"
20  *
21  * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
22  */

23
24 package com.sun.ejb.containers.util.cache;
25
26 import com.sun.appserv.util.cache.Cache;
27 import com.sun.appserv.util.cache.CacheListener;
28 import com.sun.appserv.util.cache.Constants;
29 import com.sun.logging.*;
30
31 import java.util.ArrayList JavaDoc;
32 import java.util.Map JavaDoc;
33 import java.util.Properties JavaDoc;
34 import java.util.ResourceBundle JavaDoc;
35 import java.util.logging.*;
36
37 /**
38  * LRUCache
39  * in-memory bounded cache with an LRU list
40  */

41 public class LruCache extends BaseCache {
42
43     protected static Logger _logger;
44     static {
45         _logger=LogDomains.getLogger(LogDomains.EJB_LOGGER);
46     }
47     protected String JavaDoc cacheName;
48
49     // the item never expires
50
public static final long NO_TIMEOUT = -1;
51
52     // LRU list
53
protected LruCacheItem head;
54     protected LruCacheItem tail;
55
56     // the number of times the cache was trimmed
57
protected int trimCount;
58
59     protected int listSize;
60     protected long timeout = NO_TIMEOUT;
61
62     /**
63      * default constructor
64      */

65     public LruCache() { }
66
67     /**
68      * constructor with specified timeout
69      */

70     public LruCache(long timeout) {
71         this.timeout = timeout;
72     }
73
74     /**
75      * create new item
76      * @param hashCode for the entry
77      * @param key <code>Object</code> key
78      * @param value <code>Object</code> value
79      * @param size size in bytes of the item
80      *
81      * subclasses may override to provide their own CacheItem extensions
82      * e.g. one that permits persistence.
83      */

84     protected CacheItem createItem(int hashCode, Object JavaDoc key,
85                                    Object JavaDoc value, int size) {
86         return new LruCacheItem(hashCode, key, value, size);
87     }
88
89     /**
90      * trim one item from the LRU list
91      * @param currentTime of this operation
92      * @return the item trimmed from cache
93      *
94      * list synchronization is handled by the caller
95      */

96     protected CacheItem trimLru(long currentTime) {
97
98         LruCacheItem trimItem = tail;
99
100         if (tail != head) {
101             tail = trimItem.lPrev;
102             if (tail == null) {
103                 _logger.log(Level.WARNING,
104                     "[" + cacheName + "]: trimLru(), resetting head and tail");
105                 // do not let the tail go past the head
106
tail = head = null;
107             } else {
108                 tail.lNext = null;
109             }
110         } else {
111             tail = head = null;
112         }
113         
114         if (trimItem != null) {
115             trimItem.isTrimmed = true;
116             trimItem.lPrev = null;
117             trimCount++;
118             listSize--;
119         }
120
121         return trimItem;
122     }
123
124     /**
125     /**
126      * this item is just added to the cache
127      * @param item <code>CacheItem</code> that was created
128      * @return a overflow item; may be null
129      *
130      * this function checks if adding the new item results in a overflow;
131      * if so, it returns the item to be removed.
132      *
133      * Cache bucket is already synchronized by the caller
134      */

135     protected CacheItem itemAdded(CacheItem item) {
136         CacheItem overflow = null;
137         LruCacheItem lc = (LruCacheItem) item;
138
139         // set the timestamp
140
lc.lastAccessed = System.currentTimeMillis();
141
142         // update the LRU
143
synchronized (this) {
144             if (head != null) {
145                 head.lPrev = lc;
146                 lc.lNext = head;
147         lc.lPrev = null;
148         head = lc;
149             }
150             else {
151                 head = tail = lc;
152         lc.lPrev = lc.lNext = null;
153             }
154
155             listSize++;
156
157             if ( isThresholdReached() ) {
158                 overflow = trimLru(lc.lastAccessed);
159             }
160         }
161
162         return overflow;
163     }
164
165     /**
166      * this item is accessed
167      * @param item <code>CacheItem</code> accessed
168      *
169      * Cache bucket is already synchronized by the caller
170      */

171     protected void itemAccessed(CacheItem item) {
172         LruCacheItem lc = (LruCacheItem) item;
173
174         synchronized (this) {
175
176             // if the item is already trimmed from the LRU list, nothing to do.
177
if (lc.isTrimmed)
178                 return;
179
180             // update the timestamp
181
lc.lastAccessed = System.currentTimeMillis();
182
183             LruCacheItem prev = lc.lPrev;
184             LruCacheItem next = lc.lNext;
185
186             // update the LRU list
187
if (prev != null) {
188                 // put the item at the head of LRU list
189
lc.lPrev = null;
190                 lc.lNext = head;
191                 head.lPrev = lc;
192                 head = lc;
193     
194                 // patch up the previous neighbors
195
prev.lNext = next;
196                 if (next != null)
197                     next.lPrev = prev;
198                 else
199                     tail = prev;
200            }
201         }
202     }
203
204
205     /**
206      * item value has been refreshed
207      * @param item <code>CacheItem</code> that was refreshed
208      * @param oldSize size of the previous value that was refreshed
209      * Cache bucket is already synchronized by the caller
210      */

211     protected void itemRefreshed(CacheItem item, int oldSize) {
212         itemAccessed(item);
213     }
214
215     /**
216      * item value has been removed from the cache
217      * @param item <code>CacheItem</code> that was just removed
218      *
219      * Cache bucket is already synchronized by the caller
220      */

221     protected void itemRemoved(CacheItem item) {
222         LruCacheItem l = (LruCacheItem) item;
223
224         // remove the item from the LRU list
225
synchronized (this) {
226             LruCacheItem prev = l.lPrev;
227             LruCacheItem next = l.lNext;
228
229             // if the item is already trimmed from the LRU list, nothing to do.
230
if (l.isTrimmed)
231                 return;
232
233             // patch up the neighbors and make sure head/tail are correct
234
if (prev != null)
235                 prev.lNext = next;
236             else
237                 head = next;
238
239             if (next != null)
240                 next.lPrev = prev;
241             else
242                 tail = prev;
243
244         l.lPrev = l.lNext = null;
245             listSize--;
246         }
247     }
248
249     /**
250      * trim the expired entries from the cache.
251      * @param maxCount maximum number of invalid entries to trim
252      * specify Integer.MAX_VALUE to trim all invalid entries
253      * This call is to be scheduled by a thread managed by the container.
254      *
255      * NOTE: this algorithm assumes that all the entries in the cache have
256      * identical timeout (otherwise traversing from tail won't be right).
257      */

258     public void trimExpiredEntries(int maxCount) {
259         
260         int count = 0;
261         LruCacheItem item;
262         long currentTime = System.currentTimeMillis();
263     ArrayList JavaDoc list = new ArrayList JavaDoc();
264
265         synchronized (this) {
266             // traverse LRU list till we reach a valid item;
267
// remove them at once
268
for (item = tail; item != null && count < maxCount;
269                  item = item.lPrev) {
270
271                 if ( (timeout != NO_TIMEOUT) &&
272                      ((item.lastAccessed + timeout) <= currentTime) ) {
273                     item.isTrimmed = true;
274             list.add(item);
275
276                     count++;
277                 } else {
278                     break;
279                 }
280             }
281
282             // if there was at least one invalid item then item != tail.
283
if (item != tail) {
284                 if (item != null)
285                     item.lNext = null;
286                 else
287                     head = null;
288
289                 tail = item;
290             }
291             listSize -= count;
292             trimCount += count;
293         }
294         
295         // trim the items from the BaseCache from the old tail backwards
296
for (int index=list.size()-1; index >= 0; index--) {
297             trimItem((LruCacheItem) list.get(index));
298         }
299     }
300
301     /**
302      * get generic stats from subclasses
303      */

304
305     /**
306      * get the desired statistic counter
307      * @param key to corresponding stat
308      * @return an Object corresponding to the stat
309      * See also: Constant.java for the key
310      */

311     public Object JavaDoc getStatByName(String JavaDoc key) {
312         Object JavaDoc stat = super.getStatByName(key);
313
314         if (stat == null && key != null) {
315             if (key.equals(Constants.STAT_LRUCACHE_LIST_LENGTH))
316                 stat = new Integer JavaDoc(listSize);
317             else if (key.equals(Constants.STAT_LRUCACHE_TRIM_COUNT))
318                 stat = new Integer JavaDoc(trimCount);
319         }
320         return stat;
321     }
322
323     public Map JavaDoc getStats() {
324         Map JavaDoc stats = super.getStats();
325         stats.put(Constants.STAT_LRUCACHE_LIST_LENGTH, new Integer JavaDoc(listSize));
326         stats.put(Constants.STAT_LRUCACHE_TRIM_COUNT, new Integer JavaDoc(trimCount));
327
328         return stats;
329     }
330
331     public void setCacheName(String JavaDoc name) {
332         this.cacheName = name;
333     }
334
335     /** default CacheItem class implementation ***/
336     protected static class LruCacheItem extends CacheItem {
337
338         // double linked LRU list
339
protected LruCacheItem lNext;
340         protected LruCacheItem lPrev;
341         protected boolean isTrimmed;
342         protected long lastAccessed;
343
344         protected LruCacheItem(int hashCode, Object JavaDoc key, Object JavaDoc value,
345                                int size) {
346             super(hashCode, key, value, size);
347         }
348     }
349
350 }
351
Popular Tags