package com.onionnetworks.util; import java.util.*; import java.lang.ref.*; public class TimedSoftHashMap extends HashMap { public static final int DEFAULT_TTL = 2*60*1000; TreeSet timings = new TreeSet(); public TimedSoftHashMap() { super(); } public TimedSoftHashMap(Map t) { throw new UnsupportedOperationException("this(Map t)"); } public TimedSoftHashMap(int initialCapacity) { super(initialCapacity); } public TimedSoftHashMap(int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor); } public boolean containsValue(Object value) { return super.containsValue(new HashableSoftReference(value,0)); } public Set entrySet() { throw new UnsupportedOperationException("entrySet()"); } /** * We renew the timer every time get() is called. */ public Object get(Object key) { HashableSoftReference ref = (HashableSoftReference) super.get(key); if (ref != null) { ref.renew(); } checkTimings(); return ref == null ? null : ref.get(); } public boolean isEmpty() { // In order to implement this method you need to clear out the // garbage collected entries. Shouldn't be hard but I don't have time throw new UnsupportedOperationException("isEmpty()"); } public Set keySet() { throw new UnsupportedOperationException("entrySet()"); } public Object put(Object key, Object value) { return this.put(key,value,DEFAULT_TTL); } public Object put(Object key, Object value, int ttl) { checkTimings(); HashableSoftReference hsr = new HashableSoftReference(value,ttl); HashableSoftReference hsr2 = (HashableSoftReference)super.put(key,hsr); timings.add(hsr); if (hsr2 == null) { return null; } else { timings.remove(hsr2); return hsr2.get(); } } public void putAll(Map t) { throw new UnsupportedOperationException("putAll(Map t)"); } public Object remove(Object key) { checkTimings(); Reference ref = (Reference) super.remove(key); timings.remove(ref); return ref == null ? null : ref.get(); } public int size() { // In order to implement this method you need to clear out the // garbage collected entries. Shouldn't be hard but I don't have time throw new UnsupportedOperationException("size()"); } public Collection values() { throw new UnsupportedOperationException("values()"); } public Object clone() { throw new UnsupportedOperationException("clone()"); } protected void checkTimings() { long time = System.currentTimeMillis(); for (Iterator it=timings.iterator();it.hasNext();) { HashableSoftReference hsr = (HashableSoftReference) it.next(); if (hsr.deathTime < time) { for (Iterator it2=super.keySet().iterator();it2.hasNext();) { Object key = it2.next(); if (super.get(key) == hsr) { it2.remove(); it.remove(); break; } } } else { break; } } } /** * This class is only necessary for the containsValue calls, as the * keys in the TimedSoftHashMap should not be SoftReferences and there * for should already have equals() and hashCode() that works. */ public class HashableSoftReference extends SoftReference implements Comparable { public long deathTime; public int ttl; public HashableSoftReference(Object ref, int ttl) { super(ref); this.ttl = ttl; renew(); } public void renew() { this.deathTime = System.currentTimeMillis()+ttl; } public int compareTo(Object obj) { HashableSoftReference hsr = (HashableSoftReference) obj; if (hsr.deathTime == deathTime) { return 0; } return hsr.deathTime < deathTime ? 1 : -1; } public boolean equals(Object obj) { if (obj == null) { throw new NullPointerException(); } Object thisObj = this.get(); if (thisObj == null) { return false; } else { return thisObj.equals(obj); } } public int hashCode() { Object thisObj = this.get(); if (thisObj == null) { return 0; } else { return thisObj.hashCode(); } } } }