KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > ofbiz > minerva > pool > cache > LeastRecentlyUsedCache


1 /*
2  * Licensed under the X license (see http://www.x.org/terms.htm)
3  */

4 package org.ofbiz.minerva.pool.cache;
5
6 import java.io.PrintWriter JavaDoc;
7 import java.util.*;
8
9 /**
10  * A Least Recently Used cache implementation. The object in the
11  * cache that was least recently used is dropped when the cache is
12  * full and a new request comes in. The implementation uses a linked
13  * list (to track recentness) and a HashMap (to navigate quickly).
14  * @author Aaron Mulder ammulder@alumni.princeton.edu
15  */

16 public class LeastRecentlyUsedCache implements ObjectCache {
17
18     private Object JavaDoc lock = new Object JavaDoc();
19     private HashMap keyMap = new HashMap();
20     private PrintWriter JavaDoc log = null;
21     private Node mostRecentNode, leastRecentNode;
22     private int size;
23     private int maxSize;
24     private CachedObjectFactory factory;
25
26     public LeastRecentlyUsedCache(CachedObjectFactory factory, int maxSize) {
27         this.factory = factory;
28         this.maxSize = maxSize;
29     }
30
31     public Object JavaDoc getObject(Object JavaDoc key) {
32         return getObject(key, false);
33     }
34
35     public Object JavaDoc useObject(Object JavaDoc key) {
36         return getObject(key, true);
37     }
38
39     public void returnObject(Object JavaDoc key, Object JavaDoc value) {
40         Object JavaDoc pooled = keyMap.get(key);
41         Object JavaDoc source = factory.translateObject(value);
42
43         if (pooled == null) {
44             factory.deleteObject(source);
45         } else if (pooled instanceof Node) {
46             Node node = (Node) pooled;
47             if (node.data == source) {
48                 try {
49                     node.setUsed(false);
50                 } catch (ConcurrentModificationException e) {
51                 }
52             } else {
53                 factory.deleteObject(source);
54             }
55         } else if (pooled instanceof LinkedList) {
56             for (Iterator it = ((LinkedList) pooled).iterator(); it.hasNext();) {
57                 Node node = (Node) it.next();
58                 if (node.data == source) {
59                     try {
60                         node.setUsed(false);
61                     } catch (ConcurrentModificationException e) {
62                     }
63                     return;
64                 }
65             }
66             factory.deleteObject(source);
67         } else
68             throw new Error JavaDoc("LRU Cache Assertion Failure: Wrong class '" + pooled.getClass().getName() + "' in keyMap!");
69     }
70
71     public void removeObjects(Object JavaDoc key) {
72         synchronized (lock) {
73             Object JavaDoc value = keyMap.get(key);
74             if (value == null) {
75                 return;
76             } else if (value instanceof Node) {
77                 removeNode((Node) value);
78             } else if (value instanceof LinkedList) {
79                 LinkedList list = (LinkedList) value;
80                 int max = list.size();
81                 for (int i = 0; i < max; i++) {
82                     removeNode((Node) list.get(0));
83                 }
84             }
85         }
86     }
87
88     public void setSize(int maxSize) {
89         this.maxSize = maxSize;
90         checkMaxSize();
91     }
92
93     public void close() {
94         synchronized (lock) {
95             Node current = leastRecentNode;
96             Node next = current == null ? null : current.moreRecent;
97             while (current != null) {
98                 removeNode(current);
99                 current = next;
100                 next = current == null ? null : current.moreRecent;
101             }
102         }
103     }
104
105     private Object JavaDoc getObject(Object JavaDoc key, boolean use) {
106         Node node = null;
107         try {
108             Object JavaDoc value = keyMap.get(key);
109             if (value == null) {
110                 node = addObject(key);
111             } else if (value instanceof Node) {
112                 node = (Node) value;
113                 if (!node.used) {
114                     makeMostRecent(node);
115                 } else {
116                     node = addObject(key);
117                 }
118             } else if (value instanceof LinkedList) {
119                 for (Iterator it = ((LinkedList) value).iterator(); it.hasNext();) {
120                     node = (Node) it.next();
121                     if (!node.used) {
122                         makeMostRecent(node);
123                         break;
124                     } else {
125                         node = null;
126                     }
127                 }
128                 if (node == null) {
129                     node = addObject(key);
130                 }
131             } else
132                 throw new Error JavaDoc("LRU Cache Assertion Failure: Wrong class '" + value.getClass().getName() + "' in keyMap!");
133             if (use) {
134                 node.setUsed(true);
135             }
136         } catch (ConcurrentModificationException e) {
137             return getObject(key, use);
138         }
139         return factory.prepareObject(node.data);
140     }
141
142     private Node addObject(Object JavaDoc key) {
143         Object JavaDoc data;
144         try {
145             data = factory.createObject(key);
146         } catch (Exception JavaDoc e) {
147             if (log != null)
148                 e.printStackTrace(log);
149             return null;
150         }
151         Node result;
152         synchronized (lock) {
153             if (mostRecentNode == null) {
154                 result = new Node(null, null, data);
155                 mostRecentNode = result;
156                 leastRecentNode = result;
157             } else {
158                 result = new Node(mostRecentNode, null, data);
159                 mostRecentNode.moreRecent = result;
160                 mostRecentNode = result;
161             }
162             ++size;
163             checkMaxSize();
164         }
165
166         // Put the key/value(s) and node/key in the map
167
Object JavaDoc value = keyMap.get(key);
168         if (value == null) {
169             keyMap.put(key, result);
170         } else if (value instanceof LinkedList) {
171             ((LinkedList) value).add(result);
172         } else if (value instanceof Node) {
173             LinkedList list = new LinkedList();
174             list.add(value);
175             list.add(result);
176             keyMap.put(key, list);
177         } else
178             throw new Error JavaDoc("LRU Cache Assertion Failure: Wrong class '" + value.getClass().getName() + "' in keyMap!");
179         keyMap.put(result, key);
180         return result;
181     }
182
183     private void checkMaxSize() {
184         if (maxSize <= 0)
185             return;
186         while (size > maxSize) {
187             Node drop = leastRecentNode;
188             leastRecentNode = drop.moreRecent;
189             leastRecentNode.lessRecent = null;
190             --size;
191
192             removeNode(drop);
193         }
194     }
195
196     private void makeMostRecent(Node node) {
197         synchronized (lock) {
198             if (node.moreRecent == null) {
199                 if (mostRecentNode != node)
200                     throw new ConcurrentModificationException();
201                 return;
202             }
203
204             // Prepare surrounding nodes
205
Node previous = node.moreRecent;
206             Node next = node.lessRecent;
207             previous.lessRecent = next;
208             if (next == null) {
209                 leastRecentNode = previous;
210             } else {
211                 next.moreRecent = previous;
212             }
213
214             // Prepare node and new 2nd most recent
215
node.moreRecent = null;
216             node.lessRecent = mostRecentNode;
217             mostRecentNode.moreRecent = node;
218             mostRecentNode = node;
219         }
220     }
221
222     // This should be called while holding the lock
223
private void removeNode(Node node) {
224         boolean used = node.used;
225         if (!used) {
226             node.used = true;
227         }
228         Object JavaDoc key = keyMap.remove(node);
229
230
231         Object JavaDoc value = keyMap.get(key);
232         if (value instanceof Node) {
233             keyMap.remove(key);
234         } else if (value instanceof LinkedList) {
235             LinkedList list = (LinkedList) value;
236             Iterator it = list.iterator();
237             while (it.hasNext()) {
238                 Node current = (Node) it.next();
239                 if (current == node) {
240                     it.remove();
241                     break;
242                 }
243             }
244             if (list.size() == 1) {
245                 keyMap.put(key, list.get(0));
246             }
247         } else
248             throw new Error JavaDoc("LRU Cache Assertion Failure: Wrong class '" + value.getClass().getName() + "' in keyMap!");
249         if (!used) {
250             factory.deleteObject(node.data);
251         }
252         node.moreRecent = null;
253         node.lessRecent = null;
254     }
255
256     private class Node {
257
258         Node lessRecent;
259         Node moreRecent;
260         Object JavaDoc data;
261         boolean used = false;
262
263         public Node(Node lessRecent, Node moreRecent, Object JavaDoc data) {
264             this(lessRecent, moreRecent, data, false);
265         }
266
267         public Node(Node lessRecent, Node moreRecent, Object JavaDoc data, boolean used) {
268             this.lessRecent = lessRecent;
269             this.moreRecent = moreRecent;
270             this.data = data;
271             this.used = used;
272         }
273
274         public synchronized void setUsed(boolean used) {
275             if (this.used == used)
276                 throw new ConcurrentModificationException();
277             this.used = used;
278         }
279     }
280
281 /* This is for testing only
282
283     private void printList() {
284         System.out.print("Fwd Nodes:");
285         Node node = mostRecentNode;
286         for(int i=0; i<size; i++) {
287             System.out.print(" "+node.data);
288             node = node.lessRecent;
289         }
290         System.out.println();
291         Stack stack = new Stack();
292         node = leastRecentNode;
293         for(int i=0; i<size; i++) {
294             stack.push(node.data);
295             node = node.moreRecent;
296         }
297         System.out.print("Rev Nodes:");
298         while(!stack.isEmpty())
299             System.out.print(" "+stack.pop());
300         System.out.println();
301     }
302
303     public static void main(String args[]) {
304         try {
305             java.io.BufferedReader in = new java.io.BufferedReader(new java.io.InputStreamReader(System.in));
306             LeastRecentlyUsedCache cache = new LeastRecentlyUsedCache(new CachedObjectFactory() {
307                 public Object createObject(Object identifier) {
308                     return "v"+identifier;
309                 }
310                 public void deleteObject(Object object) {
311                     System.out.println("Deleting Object "+object);
312                 }
313             }, 5);
314
315             * *** THIS TESTS getObject ****
316
317             while(true) {
318                 cache.printList();
319                 System.out.print("> ");
320                 System.out.flush();
321                 String key = in.readLine();
322                 if(key.equalsIgnoreCase("quit") || key.equalsIgnoreCase("exit"))
323                     return;
324                 Object value = cache.getObject(key);
325                 System.out.println("Got Value: "+value);
326             }
327
328             * ********* *
329
330             **** THIS TESTS useObject AND returnObject ****
331
332             Object lastKey = null, last = null;
333             while(true) {
334                 cache.printList();
335                 System.out.print("> ");
336                 System.out.flush();
337                 String key = in.readLine();
338                 if(key.equalsIgnoreCase("quit") || key.equalsIgnoreCase("exit"))
339                     return;
340                 Object value = cache.useObject(key);
341                 if(last != null) {
342                     cache.returnObject(lastKey, last);
343                 }
344                 lastKey = key;
345                 last = value;
346                 System.out.println("Got Value: "+value);
347             }
348
349             ********** *
350         } catch(Throwable e) {
351             e.printStackTrace();
352         }
353     }
354 */

355 }
356
Popular Tags