History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/security/util/Cache.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/security/util/Cache.java+542−0
@@ -0,0 +1,542 @@
1+/*
2+ * Copyright (c) 2002, 2009, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+
26+package sun.security.util;
27+
28+import java.util.*;
29+import java.lang.ref.*;
30+
31+/**
32+ * Abstract base class and factory for caches. A cache is a key-value mapping.
33+ * It has properties that make it more suitable for caching than a Map.
34+ *
35+ * The factory methods can be used to obtain two different implementations.
36+ * They have the following properties:
37+ *
38+ * . keys and values reside in memory
39+ *
40+ * . keys and values must be non-null
41+ *
42+ * . maximum size. Replacements are made in LRU order.
43+ *
44+ * . optional lifetime, specified in seconds.
45+ *
46+ * . save for concurrent use by multiple threads
47+ *
48+ * . values are held by either standard references or via SoftReferences.
49+ * SoftReferences have the advantage that they are automatically cleared
50+ * by the garbage collector in response to memory demand. This makes it
51+ * possible to simple set the maximum size to a very large value and let
52+ * the GC automatically size the cache dynamically depending on the
53+ * amount of available memory.
54+ *
55+ * However, note that because of the way SoftReferences are implemented in
56+ * HotSpot at the moment, this may not work perfectly as it clears them fairly
57+ * eagerly. Performance may be improved if the Java heap size is set to larger
58+ * value using e.g. java -ms64M -mx128M foo.Test
59+ *
60+ * Cache sizing: the memory cache is implemented on top of a LinkedHashMap.
61+ * In its current implementation, the number of buckets (NOT entries) in
62+ * (Linked)HashMaps is always a power of two. It is recommended to set the
63+ * maximum cache size to value that uses those buckets fully. For example,
64+ * if a cache with somewhere between 500 and 1000 entries is desired, a
65+ * maximum size of 750 would be a good choice: try 1024 buckets, with a
66+ * load factor of 0.75f, the number of entries can be calculated as
67+ * buckets / 4 * 3. As mentioned above, with a SoftReference cache, it is
68+ * generally reasonable to set the size to a fairly large value.
69+ *
70+ * @author Andreas Sterbenz
71+ */
72+public abstract class Cache {
73+
74+ protected Cache() {
75+ // empty
76+ }
77+
78+ /**
79+ * Return the number of currently valid entries in the cache.
80+ */
81+ public abstract int size();
82+
83+ /**
84+ * Remove all entries from the cache.
85+ */
86+ public abstract void clear();
87+
88+ /**
89+ * Add an entry to the cache.
90+ */
91+ public abstract void put(Object key, Object value);
92+
93+ /**
94+ * Get a value from the cache.
95+ */
96+ public abstract Object get(Object key);
97+
98+ /**
99+ * Remove an entry from the cache.
100+ */
101+ public abstract void remove(Object key);
102+
103+ /**
104+ * Set the maximum size.
105+ */
106+ public abstract void setCapacity(int size);
107+
108+ /**
109+ * Set the timeout(in seconds).
110+ */
111+ public abstract void setTimeout(int timeout);
112+
113+ /**
114+ * accept a visitor
115+ */
116+ public abstract void accept(CacheVisitor visitor);
117+
118+ /**
119+ * Return a new memory cache with the specified maximum size, unlimited
120+ * lifetime for entries, with the values held by SoftReferences.
121+ */
122+ public static Cache newSoftMemoryCache(int size) {
123+ return new MemoryCache(true, size);
124+ }
125+
126+ /**
127+ * Return a new memory cache with the specified maximum size, the
128+ * specified maximum lifetime (in seconds), with the values held
129+ * by SoftReferences.
130+ */
131+ public static Cache newSoftMemoryCache(int size, int timeout) {
132+ return new MemoryCache(true, size, timeout);
133+ }
134+
135+ /**
136+ * Return a new memory cache with the specified maximum size, unlimited
137+ * lifetime for entries, with the values held by standard references.
138+ */
139+ public static Cache newHardMemoryCache(int size) {
140+ return new MemoryCache(false, size);
141+ }
142+
143+ /**
144+ * Return a dummy cache that does nothing.
145+ */
146+ public static Cache newNullCache() {
147+ return NullCache.INSTANCE;
148+ }
149+
150+ /**
151+ * Return a new memory cache with the specified maximum size, the
152+ * specified maximum lifetime (in seconds), with the values held
153+ * by standard references.
154+ */
155+ public static Cache newHardMemoryCache(int size, int timeout) {
156+ return new MemoryCache(false, size, timeout);
157+ }
158+
159+ /**
160+ * Utility class that wraps a byte array and implements the equals()
161+ * and hashCode() contract in a way suitable for Maps and caches.
162+ */
163+ public static class EqualByteArray {
164+
165+ private final byte[] b;
166+ private volatile int hash;
167+
168+ public EqualByteArray(byte[] b) {
169+ this.b = b;
170+ }
171+
172+ public int hashCode() {
173+ int h = hash;
174+ if (h == 0) {
175+ h = b.length + 1;
176+ for (int i = 0; i < b.length; i++) {
177+ h += (b[i] & 0xff) * 37;
178+ }
179+ hash = h;
180+ }
181+ return h;
182+ }
183+
184+ public boolean equals(Object obj) {
185+ if (this == obj) {
186+ return true;
187+ }
188+ if (obj instanceof EqualByteArray == false) {
189+ return false;
190+ }
191+ EqualByteArray other = (EqualByteArray)obj;
192+ return Arrays.equals(this.b, other.b);
193+ }
194+ }
195+
196+ public interface CacheVisitor {
197+ public void visit(Map<Object, Object> map);
198+ }
199+
200+}
201+
202+class NullCache extends Cache {
203+
204+ final static Cache INSTANCE = new NullCache();
205+
206+ private NullCache() {
207+ // empty
208+ }
209+
210+ public int size() {
211+ return 0;
212+ }
213+
214+ public void clear() {
215+ // empty
216+ }
217+
218+ public void put(Object key, Object value) {
219+ // empty
220+ }
221+
222+ public Object get(Object key) {
223+ return null;
224+ }
225+
226+ public void remove(Object key) {
227+ // empty
228+ }
229+
230+ public void setCapacity(int size) {
231+ // empty
232+ }
233+
234+ public void setTimeout(int timeout) {
235+ // empty
236+ }
237+
238+ public void accept(CacheVisitor visitor) {
239+ // empty
240+ }
241+
242+}
243+
244+class MemoryCache extends Cache {
245+
246+ private final static float LOAD_FACTOR = 0.75f;
247+
248+ // XXXX
249+ private final static boolean DEBUG = false;
250+
251+ private final Map<Object, CacheEntry> cacheMap;
252+ private int maxSize;
253+ private long lifetime;
254+ private final ReferenceQueue queue;
255+
256+ public MemoryCache(boolean soft, int maxSize) {
257+ this(soft, maxSize, 0);
258+ }
259+
260+ public MemoryCache(boolean soft, int maxSize, int lifetime) {
261+ this.maxSize = maxSize;
262+ this.lifetime = lifetime * 1000;
263+ this.queue = soft ? new ReferenceQueue() : null;
264+ int buckets = (int)(maxSize / LOAD_FACTOR) + 1;
265+ cacheMap = new LinkedHashMap<Object, CacheEntry>(buckets,
266+ LOAD_FACTOR, true);
267+ }
268+
269+ /**
270+ * Empty the reference queue and remove all corresponding entries
271+ * from the cache.
272+ *
273+ * This method should be called at the beginning of each public
274+ * method.
275+ */
276+ private void emptyQueue() {
277+ if (queue == null) {
278+ return;
279+ }
280+ int startSize = cacheMap.size();
281+ while (true) {
282+ CacheEntry entry = (CacheEntry)queue.poll();
283+ if (entry == null) {
284+ break;
285+ }
286+ Object key = entry.getKey();
287+ if (key == null) {
288+ // key is null, entry has already been removed
289+ continue;
290+ }
291+ CacheEntry currentEntry = cacheMap.remove(key);
292+ // check if the entry in the map corresponds to the expired
293+ // entry. If not, readd the entry
294+ if ((currentEntry != null) && (entry != currentEntry)) {
295+ cacheMap.put(key, currentEntry);
296+ }
297+ }
298+ if (DEBUG) {
299+ int endSize = cacheMap.size();
300+ if (startSize != endSize) {
301+ System.out.println("*** Expunged " + (startSize - endSize)
302+ + " entries, " + endSize + " entries left");
303+ }
304+ }
305+ }
306+
307+ /**
308+ * Scan all entries and remove all expired ones.
309+ */
310+ private void expungeExpiredEntries() {
311+ emptyQueue();
312+ if (lifetime == 0) {
313+ return;
314+ }
315+ int cnt = 0;
316+ long time = System.currentTimeMillis();
317+ for (Iterator<CacheEntry> t = cacheMap.values().iterator();
318+ t.hasNext(); ) {
319+ CacheEntry entry = t.next();
320+ if (entry.isValid(time) == false) {
321+ t.remove();
322+ cnt++;
323+ }
324+ }
325+ if (DEBUG) {
326+ if (cnt != 0) {
327+ System.out.println("Removed " + cnt
328+ + " expired entries, remaining " + cacheMap.size());
329+ }
330+ }
331+ }
332+
333+ public synchronized int size() {
334+ expungeExpiredEntries();
335+ return cacheMap.size();
336+ }
337+
338+ public synchronized void clear() {
339+ if (queue != null) {
340+ // if this is a SoftReference cache, first invalidate() all
341+ // entries so that GC does not have to enqueue them
342+ for (CacheEntry entry : cacheMap.values()) {
343+ entry.invalidate();
344+ }
345+ while (queue.poll() != null) {
346+ // empty
347+ }
348+ }
349+ cacheMap.clear();
350+ }
351+
352+ public synchronized void put(Object key, Object value) {
353+ emptyQueue();
354+ long expirationTime = (lifetime == 0) ? 0 :
355+ System.currentTimeMillis() + lifetime;
356+ CacheEntry newEntry = newEntry(key, value, expirationTime, queue);
357+ CacheEntry oldEntry = cacheMap.put(key, newEntry);
358+ if (oldEntry != null) {
359+ oldEntry.invalidate();
360+ return;
361+ }
362+ if (maxSize > 0 && cacheMap.size() > maxSize) {
363+ expungeExpiredEntries();
364+ if (cacheMap.size() > maxSize) { // still too large?
365+ Iterator<CacheEntry> t = cacheMap.values().iterator();
366+ CacheEntry lruEntry = t.next();
367+ if (DEBUG) {
368+ System.out.println("** Overflow removal "
369+ + lruEntry.getKey() + " | " + lruEntry.getValue());
370+ }
371+ t.remove();
372+ lruEntry.invalidate();
373+ }
374+ }
375+ }
376+
377+ public synchronized Object get(Object key) {
378+ emptyQueue();
379+ CacheEntry entry = cacheMap.get(key);
380+ if (entry == null) {
381+ return null;
382+ }
383+ long time = (lifetime == 0) ? 0 : System.currentTimeMillis();
384+ if (entry.isValid(time) == false) {
385+ if (DEBUG) {
386+ System.out.println("Ignoring expired entry");
387+ }
388+ cacheMap.remove(key);
389+ return null;
390+ }
391+ return entry.getValue();
392+ }
393+
394+ public synchronized void remove(Object key) {
395+ emptyQueue();
396+ CacheEntry entry = cacheMap.remove(key);
397+ if (entry != null) {
398+ entry.invalidate();
399+ }
400+ }
401+
402+ public synchronized void setCapacity(int size) {
403+ expungeExpiredEntries();
404+ if (size > 0 && cacheMap.size() > size) {
405+ Iterator<CacheEntry> t = cacheMap.values().iterator();
406+ for (int i = cacheMap.size() - size; i > 0; i--) {
407+ CacheEntry lruEntry = t.next();
408+ if (DEBUG) {
409+ System.out.println("** capacity reset removal "
410+ + lruEntry.getKey() + " | " + lruEntry.getValue());
411+ }
412+ t.remove();
413+ lruEntry.invalidate();
414+ }
415+ }
416+
417+ maxSize = size > 0 ? size : 0;
418+
419+ if (DEBUG) {
420+ System.out.println("** capacity reset to " + size);
421+ }
422+ }
423+
424+ public synchronized void setTimeout(int timeout) {
425+ emptyQueue();
426+ lifetime = timeout > 0 ? timeout * 1000L : 0L;
427+
428+ if (DEBUG) {
429+ System.out.println("** lifetime reset to " + timeout);
430+ }
431+ }
432+
433+ // it is a heavyweight method.
434+ public synchronized void accept(CacheVisitor visitor) {
435+ expungeExpiredEntries();
436+ Map<Object, Object> cached = getCachedEntries();
437+
438+ visitor.visit(cached);
439+ }
440+
441+ private Map<Object, Object> getCachedEntries() {
442+ Map<Object,Object> kvmap = new HashMap<Object,Object>(cacheMap.size());
443+
444+ for (CacheEntry entry : cacheMap.values()) {
445+ kvmap.put(entry.getKey(), entry.getValue());
446+ }
447+
448+ return kvmap;
449+ }
450+
451+ protected CacheEntry newEntry(Object key, Object value,
452+ long expirationTime, ReferenceQueue queue) {
453+ if (queue != null) {
454+ return new SoftCacheEntry(key, value, expirationTime, queue);
455+ } else {
456+ return new HardCacheEntry(key, value, expirationTime);
457+ }
458+ }
459+
460+ private static interface CacheEntry {
461+
462+ boolean isValid(long currentTime);
463+
464+ void invalidate();
465+
466+ Object getKey();
467+
468+ Object getValue();
469+
470+ }
471+
472+ private static class HardCacheEntry implements CacheEntry {
473+
474+ private Object key, value;
475+ private long expirationTime;
476+
477+ HardCacheEntry(Object key, Object value, long expirationTime) {
478+ this.key = key;
479+ this.value = value;
480+ this.expirationTime = expirationTime;
481+ }
482+
483+ public Object getKey() {
484+ return key;
485+ }
486+
487+ public Object getValue() {
488+ return value;
489+ }
490+
491+ public boolean isValid(long currentTime) {
492+ boolean valid = (currentTime <= expirationTime);
493+ if (valid == false) {
494+ invalidate();
495+ }
496+ return valid;
497+ }
498+
499+ public void invalidate() {
500+ key = null;
501+ value = null;
502+ expirationTime = -1;
503+ }
504+ }
505+
506+ private static class SoftCacheEntry
507+ extends SoftReference implements CacheEntry {
508+
509+ private Object key;
510+ private long expirationTime;
511+
512+ SoftCacheEntry(Object key, Object value, long expirationTime,
513+ ReferenceQueue queue) {
514+ super(value, queue);
515+ this.key = key;
516+ this.expirationTime = expirationTime;
517+ }
518+
519+ public Object getKey() {
520+ return key;
521+ }
522+
523+ public Object getValue() {
524+ return get();
525+ }
526+
527+ public boolean isValid(long currentTime) {
528+ boolean valid = (currentTime <= expirationTime) && (get() != null);
529+ if (valid == false) {
530+ invalidate();
531+ }
532+ return valid;
533+ }
534+
535+ public void invalidate() {
536+ clear();
537+ key = null;
538+ expirationTime = -1;
539+ }
540+ }
541+
542+}