1 /* 2 * Copyright (c) 2002, 2011, 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 /* 27 */ 28 29 package java.io; 30 31 import java.util.Iterator; 32 import java.util.Map; 33 import java.util.LinkedHashMap; 34 import java.util.Set; 35 36 class ExpiringCache { 37 private long millisUntilExpiration; 38 private Map<String,Entry> map; 39 // Clear out old entries every few queries 40 private int queryCount; 41 private int queryOverflow = 300; 42 private int MAX_ENTRIES = 200; 43 44 static class Entry { 45 private long timestamp; 46 private String val; 47 48 Entry(long timestamp, String val) { 49 this.timestamp = timestamp; 50 this.val = val; 51 } 52 53 long timestamp() { return timestamp; } 54 void setTimestamp(long timestamp) { this.timestamp = timestamp; } 55 56 String val() { return val; } 57 void setVal(String val) { this.val = val; } 58 } 59 60 ExpiringCache() { 61 this(30000); 62 } 63 64 @SuppressWarnings("serial") 65 ExpiringCache(long millisUntilExpiration) { 66 this.millisUntilExpiration = millisUntilExpiration; 67 map = new LinkedHashMap<>() { 68 protected boolean removeEldestEntry(Map.Entry<String,Entry> eldest) { 69 return size() > MAX_ENTRIES; 70 } 71 }; 72 } 73 74 synchronized String get(String key) { 75 if (++queryCount >= queryOverflow) { 76 cleanup(); 77 } 78 Entry entry = entryFor(key); 79 if (entry != null) { 80 return entry.val(); 81 } 82 return null; 83 } 84 85 synchronized void put(String key, String val) { 86 if (++queryCount >= queryOverflow) { 87 cleanup(); 88 } 89 Entry entry = entryFor(key); 90 if (entry != null) { 91 entry.setTimestamp(System.currentTimeMillis()); 92 entry.setVal(val); 93 } else { 94 map.put(key, new Entry(System.currentTimeMillis(), val)); 95 } 96 } 97 98 synchronized void clear() { 99 map.clear(); 100 } 101 102 private Entry entryFor(String key) { 103 Entry entry = map.get(key); 104 if (entry != null) { 105 long delta = System.currentTimeMillis() - entry.timestamp(); 106 if (delta < 0 || delta >= millisUntilExpiration) { 107 map.remove(key); 108 entry = null; 109 } 110 } 111 return entry; 112 } 113 114 private void cleanup() { 115 Set<String> keySet = map.keySet(); 116 // Avoid ConcurrentModificationExceptions 117 String[] keys = new String[keySet.size()]; 118 int i = 0; 119 for (String key: keySet) { 120 keys[i++] = key; 121 } 122 for (int j = 0; j < keys.length; j++) { 123 entryFor(keys[j]); 124 } 125 queryCount = 0; 126 } 127 }