KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > freemarker > cache > MruCacheStorage


1 /*
2  * Copyright (c) 2003 The Visigoth Software Society. All rights
3  * reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in
14  * the documentation and/or other materials provided with the
15  * distribution.
16  *
17  * 3. The end-user documentation included with the redistribution, if
18  * any, must include the following acknowledgement:
19  * "This product includes software developed by the
20  * Visigoth Software Society (http://www.visigoths.org/)."
21  * Alternately, this acknowledgement may appear in the software itself,
22  * if and wherever such third-party acknowledgements normally appear.
23  *
24  * 4. Neither the name "FreeMarker", "Visigoth", nor any of the names of the
25  * project contributors may be used to endorse or promote products derived
26  * from this software without prior written permission. For written
27  * permission, please contact visigoths@visigoths.org.
28  *
29  * 5. Products derived from this software may not be called "FreeMarker" or "Visigoth"
30  * nor may "FreeMarker" or "Visigoth" appear in their names
31  * without prior written permission of the Visigoth Software Society.
32  *
33  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
34  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
35  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
36  * DISCLAIMED. IN NO EVENT SHALL THE VISIGOTH SOFTWARE SOCIETY OR
37  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
38  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
39  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
40  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
41  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
42  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
43  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44  * SUCH DAMAGE.
45  * ====================================================================
46  *
47  * This software consists of voluntary contributions made by many
48  * individuals on behalf of the Visigoth Software Society. For more
49  * information on the Visigoth Software Society, please see
50  * http://www.visigoths.org/
51  */

52
53 package freemarker.cache;
54
55 import java.lang.ref.ReferenceQueue JavaDoc;
56 import java.lang.ref.SoftReference JavaDoc;
57 import java.util.HashMap JavaDoc;
58 import java.util.Map JavaDoc;
59
60 /**
61  * A cache storage that implements a two-level Most Recently Used cache. In the
62  * first level, items are strongly referenced up to the specified maximum. When
63  * the maximum is exceeded, the least recently used item is moved into the
64  * second level cache, where they are softly referenced, up to another specified
65  * maximum. When the second level maximum is also exceeded, the least recently
66  * used item is discarded altogether. This cache storage is a generalization of
67  * both {@link StrongCacheStorage} and {@link SoftCacheStorage} - the effect of
68  * both of them can be achieved by setting one maximum to zero and the other to
69  * the largest positive integer.
70  * This class is <em>NOT</em> thread-safe. If it is accessed from multiple
71  * threads concurrently, proper synchronization must be provided by the callers.
72  * Note that {@link TemplateCache}, the natural user of this class provides the
73  * necessary synchronizations when it uses the class.
74  * @author Attila Szegedi
75  * @version $Id: MruCacheStorage.java,v 1.7 2003/12/18 16:20:07 ddekany Exp $
76  */

77 public class MruCacheStorage implements CacheStorage
78 {
79     private final MruEntry strongHead = new MruEntry();
80     private final MruEntry softHead = new MruEntry();
81     {
82         softHead.linkAfter(strongHead);
83     }
84     private final Map JavaDoc map = new HashMap JavaDoc();
85     private final ReferenceQueue JavaDoc refQueue = new ReferenceQueue JavaDoc();
86     private final int maxStrongSize;
87     private final int maxSoftSize;
88     private int strongSize = 0;
89     private int softSize = 0;
90     
91     /**
92      * Creates a new MRU cache storage with specified maximum cache sizes. Each
93      * cache size can vary between 0 and {@link Integer#MAX_VALUE}.
94      * @param maxStrongSize the maximum number of strongly referenced templates
95      * @param maxSoftSize the maximum number of softly referenced templates
96      */

97     public MruCacheStorage(int maxStrongSize, int maxSoftSize) {
98         if(maxStrongSize < 0) throw new IllegalArgumentException JavaDoc("maxStrongSize < 0");
99         if(maxSoftSize < 0) throw new IllegalArgumentException JavaDoc("maxSoftSize < 0");
100         this.maxStrongSize = maxStrongSize;
101         this.maxSoftSize = maxSoftSize;
102     }
103     
104     public Object JavaDoc get(Object JavaDoc key) {
105         removeClearedReferences();
106         MruEntry entry = (MruEntry)map.get(key);
107         if(entry == null) {
108             return null;
109         }
110         relinkEntryAfterStrongHead(entry, null);
111         Object JavaDoc value = entry.getValue();
112         if(value instanceof MruReference) {
113             // This can only happen with maxStrongSize == 0
114
return ((MruReference)value).get();
115         }
116         return value;
117     }
118
119     public void put(Object JavaDoc key, Object JavaDoc value) {
120         removeClearedReferences();
121         MruEntry entry = (MruEntry)map.get(key);
122         if(entry == null) {
123             entry = new MruEntry(key, value);
124             map.put(key, entry);
125             linkAfterStrongHead(entry);
126         }
127         else {
128             relinkEntryAfterStrongHead(entry, value);
129         }
130         
131     }
132
133     public void remove(Object JavaDoc key) {
134         removeClearedReferences();
135         MruEntry entry = (MruEntry)map.remove(key);
136         if(entry != null) {
137             unlinkEntryAndInspectIfSoft(entry);
138         }
139     }
140
141     public void clear() {
142         strongHead.makeHead();
143         softHead.linkAfter(strongHead);
144         map.clear();
145         strongSize = softSize = 0;
146         // Quick refQueue processing
147
while(refQueue.poll() != null);
148     }
149
150     private void relinkEntryAfterStrongHead(MruEntry entry, Object JavaDoc newValue) {
151         if(unlinkEntryAndInspectIfSoft(entry) && newValue == null) {
152             // Turn soft reference into strong reference, unless is was cleared
153
MruReference mref = (MruReference)entry.getValue();
154             Object JavaDoc strongValue = mref.get();
155             if (strongValue != null) {
156                 entry.setValue(strongValue);
157                 linkAfterStrongHead(entry);
158             } else {
159                 map.remove(mref.getKey());
160             }
161         } else {
162             if (newValue != null) {
163                 entry.setValue(newValue);
164             }
165             linkAfterStrongHead(entry);
166         }
167     }
168
169     private void linkAfterStrongHead(MruEntry entry) {
170         entry.linkAfter(strongHead);
171         if(strongSize == maxStrongSize) {
172             // softHead.previous is LRU strong entry
173
MruEntry lruStrong = softHead.getPrevious();
174             // Attila: This is equaivalent to maxStrongSize != 0
175
// DD: But entry.linkAfter(strongHead) was just excuted above, so
176
// lruStrong != strongHead is true even if maxStrongSize == 0.
177
if(lruStrong != strongHead) {
178                 lruStrong.unlink();
179                 if(maxSoftSize > 0) {
180                     lruStrong.linkAfter(softHead);
181                     lruStrong.setValue(new MruReference(lruStrong, refQueue));
182                     if(softSize == maxSoftSize) {
183                         // List is circular, so strongHead.previous is LRU soft entry
184
MruEntry lruSoft = strongHead.getPrevious();
185                         lruSoft.unlink();
186                         map.remove(lruSoft.getKey());
187                     }
188                     else {
189                         ++softSize;
190                     }
191                 }
192                 else {
193                     map.remove(lruStrong.getKey());
194                 }
195             }
196         }
197         else {
198             ++strongSize;
199         }
200     }
201
202     private boolean unlinkEntryAndInspectIfSoft(MruEntry entry) {
203         entry.unlink();
204         if(entry.getValue() instanceof MruReference) {
205             --softSize;
206             return true;
207         }
208         else {
209             --strongSize;
210             return false;
211         }
212     }
213     
214     private void removeClearedReferences() {
215         for(;;) {
216             MruReference ref = (MruReference)refQueue.poll();
217             if(ref == null) {
218                 break;
219             }
220             remove(ref.getKey());
221         }
222     }
223     
224     private static final class MruEntry
225     {
226         private MruEntry prev;
227         private MruEntry next;
228         private final Object JavaDoc key;
229         private Object JavaDoc value;
230         
231         /**
232          * Used solely to construct the head element
233          */

234         MruEntry()
235         {
236             makeHead();
237             key = value = null;
238         }
239         
240         MruEntry(Object JavaDoc key, Object JavaDoc value) {
241             this.key = key;
242             this.value = value;
243         }
244         
245         Object JavaDoc getKey() {
246             return key;
247         }
248         
249         Object JavaDoc getValue() {
250             return value;
251         }
252         
253         void setValue(Object JavaDoc value) {
254             this.value = value;
255         }
256
257         MruEntry getPrevious() {
258             return prev;
259         }
260         
261         void linkAfter(MruEntry entry) {
262             next = entry.next;
263             entry.next = this;
264             prev = entry;
265             next.prev = this;
266         }
267         
268         void unlink() {
269             next.prev = prev;
270             prev.next = next;
271             prev = null;
272             next = null;
273         }
274         
275         void makeHead() {
276             prev = next = this;
277         }
278     }
279     
280     private static class MruReference extends SoftReference JavaDoc
281     {
282         private final Object JavaDoc key;
283         
284         MruReference(MruEntry entry, ReferenceQueue JavaDoc queue) {
285             super(entry.getValue(), queue);
286             this.key = entry.getKey();
287         }
288         
289         Object JavaDoc getKey() {
290             return key;
291         }
292     }
293 }
Popular Tags