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
26package sun.security.util;
27
28import java.util.*;
29import 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 */
72public 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
202class 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
244class 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}