KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > turbo > Memory


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19 package org.netbeans.modules.turbo;
20
21 import java.util.*;
22
23 /**
24  * Keeps entity key => attribute,value pairs without actually
25  * holding key's reference. It supports several size strategies:
26  * <ul>
27  * <li>minimal entry count (for caches with short lived keys),
28  * <li>maximum entry count (for caches with long lived keys) and
29  * <li>driven by key lifetime
30  * </ul>
31  * <p>
32  * It's synchronized for safety because at least
33  * <code>TrackingRef.run</code> comes from private OpenAPI
34  * thread.
35  *
36  * @author Petr Kuzel
37  */

38 final class Memory {
39
40     /** Defines minimum entries count limit. */
41     private final int minimumSize;
42
43     /** Defines maximum entries count limit. */
44     private final int maximumSize;
45
46     /** Limited size Map&lt;key, Map>. It defines minimal cache size. */
47     private final Map minimalMap;
48
49     /** Unbound Map&lt;key, Map&lt;attributeName, attributeValue>>. Key identifies live entity. */
50     private final Map liveEntitiesMap = new WeakHashMap(3571);
51
52     /** Special value, known null that does not invalidate but caches. */
53     public static final Object JavaDoc NULL = new Object JavaDoc();
54
55     /** Keep reference to last isPrepared result. */
56     public static final ThreadLocal JavaDoc prepared = new ThreadLocal JavaDoc();
57
58     private final Statistics statistics;
59
60     private static final Random random = new Random(0);
61
62     /**
63      * Creates memory map with given strategy.
64      * @param minSize minimum size
65      * @param maxSize maximum size or <code>-1</code> for unbound size
66      * defined by key instance lifetime
67      */

68     public Memory(Statistics statistics, int minSize, int maxSize) {
69         if (maxSize != -1) {
70             if (maxSize < minSize || maxSize <1) {
71                 throw new IllegalArgumentException JavaDoc();
72             }
73         }
74         minimumSize = minSize;
75         maximumSize = maxSize;
76         minimalMap = new LRU(minimumSize);
77         this.statistics = statistics;
78     }
79
80     /**
81      * Makes best efford to store file object attributes obtainable by next {@link #get}.
82      * @param value updated value, <code>null</code> for removing memory entry
83      * or <code>Memory.NULL</code> for storing <code>null</code> value (it's known that
84      * value does not exist) later returned by {@link #get}.
85      */

86     public synchronized void put(Object JavaDoc key, String JavaDoc name, Object JavaDoc value) {
87
88         // find values map
89

90         Map attributes;
91         if (liveEntitiesMap.containsKey(key)) {
92             attributes = (Map) liveEntitiesMap.get(key);
93         } else {
94             attributes = (Map) minimalMap.get(key);
95             if (attributes == null) {
96                 attributes = new HashMap(5);
97             }
98         }
99
100         // update it
101

102         if (value != null) {
103             attributes.put(name, normalizeValue(value));
104         } else {
105             attributes.remove(name);
106         }
107         putLive(key, attributes);
108         minimalMap.put(key, attributes);
109         Entry entry = (Entry) prepared.get();
110         if (entry != null) {
111             if (key.equals(entry.key) && name.equals(entry.name)) {
112                 if (value != null) {
113                     entry.value = normalizeValue(value);
114                 } else {
115                     prepared.set(null);
116                 }
117             }
118         }
119     }
120
121     private void putLive(Object JavaDoc key, Map attributes) {
122
123         // enforce maximumSize strategy by randomly removing
124
// several (7) entries on reaching the max
125
if (maximumSize != -1 && liveEntitiesMap.size() >= maximumSize) {
126             Set keySet = liveEntitiesMap.keySet();
127             List l = new ArrayList(liveEntitiesMap.keySet());
128             
129             // liveEntitiesMap is a weakhashmap so l.size() should be the real size
130
if(l.size() == maximumSize) {
131                 int limit = Math.min(7, maximumSize/10);
132                 for (int i = 0; i<limit; i++) {
133                     int index = random.nextInt(maximumSize);
134                     Object JavaDoc removed = l.get(index);
135                     if (keySet.remove(removed)) {
136                         statistics.keyRemoved(removed);
137                     }
138                 }
139             }
140             
141         }
142
143         liveEntitiesMap.put(key, attributes);
144         statistics.keyAdded(key);
145     }
146     
147     private static Object JavaDoc normalizeValue(Object JavaDoc value) {
148         if (value == NULL) return null;
149         return value;
150     }
151
152     /**
153      * Looks for cached file atribute of given name.
154      * Return stored attributes or <code>null</code>.
155      */

156     public synchronized Object JavaDoc get(Object JavaDoc key, String JavaDoc name) {
157
158         Map attributes = (Map) liveEntitiesMap.get(key);
159         if (attributes != null) {
160             return attributes.get(name);
161         }
162
163         // try the minimal map
164
attributes = (Map) minimalMap.get(key);
165         if (attributes != null) {
166             putLive(key, attributes);
167             return attributes.get(name);
168         }
169
170         // have not been promised by existsEntry but eliminated by GC?
171
Entry entry = (Entry) prepared.get();
172         if (entry != null) {
173             if (key.equals(entry.key) && name.equals(entry.name)) {
174                 prepared.set(null); // here ends our promised contract
175
return entry.value;
176             }
177         }
178
179         return null;
180     }
181
182     /**
183      * Determines if given attribute has a cache entry.
184      * Note that the entry can contain info that attribute
185      * does not exist!
186      */

187     public synchronized boolean existsEntry(Object JavaDoc key, String JavaDoc name) {
188         Map attributes = (Map) liveEntitiesMap.get(key);
189         if (attributes == null) {
190             attributes = (Map) minimalMap.get(key);
191         }
192
193         // keep promised value in tread local to survive paralell GC
194
boolean isPrepared = attributes != null && attributes.keySet().contains(name);
195         if (isPrepared) {
196             Entry entry = (Entry) prepared.get();
197             if (entry == null) {
198                 entry = new Entry();
199             }
200             entry.key = key;
201             entry.name = name;
202             entry.value = attributes.get(name);
203             prepared.set(entry);
204         } else {
205             statistics.computeRemoved(liveEntitiesMap.keySet());
206             prepared.set(null);
207         }
208         return isPrepared;
209     }
210
211     public synchronized Object JavaDoc getMonitoredKey(Object JavaDoc key) {
212         Set keySet = liveEntitiesMap.keySet();
213         if (keySet.contains(key)) {
214             Iterator it = keySet.iterator();
215             while (it.hasNext()) {
216                 Object JavaDoc next = it.next();
217                 if (key.equals(next)) {
218                     return next;
219                 }
220             }
221         }
222         return null;
223     }
224
225     /** Single entry structure. */
226     private class Entry {
227         private Object JavaDoc key;
228         private String JavaDoc name;
229         private Object JavaDoc value;
230     }
231
232     /** Limited size LRU map implementation. */
233     private final static class LRU extends LinkedHashMap {
234
235         private final int maxSize;
236
237         public LRU(int maxSize) {
238             super(maxSize *2, 0.5f, true);
239             this.maxSize = maxSize;
240         }
241
242         protected boolean removeEldestEntry(Map.Entry eldest) {
243             return size() > maxSize;
244         }
245     }
246
247 }
248
Popular Tags