/* * ReferenceMap.java * * Created on December 11, 2006, 9:58 PM * * To change this template, choose Tools | Template Manager * and open the template in the editor. */ package org.jboss.el.util; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * * @author jhook */ public abstract class ReferenceCache { public abstract class ReferenceFactory { public abstract ReferenceKey createKey(ReferenceQueue queue, K key); public abstract ReferenceValue createValue(ReferenceQueue queue, V value); } private class StrongReferenceFactory extends ReferenceFactory { public ReferenceValue createValue(ReferenceQueue queue, final V value) { return new ReferenceValue() { public V get() { return value; } }; } public ReferenceKey createKey(ReferenceQueue queue, final K key) { return new ReferenceKey(key) { public K get() { return key; } }; } } private class WeakReferenceFactory extends ReferenceFactory { private class WeakReferenceKey extends ReferenceKey { private final WeakReference ref; public WeakReferenceKey(final ReferenceQueue queue, final K key) { super(key); this.ref = new WeakReference(key, queue) { public void clear() { remove(); super.clear(); } }; } public K get() { return this.ref.get(); } } public ReferenceValue createValue(final ReferenceQueue queue, final V value) { return new ReferenceValue() { private final WeakReference ref = new WeakReference(value, queue); public V get() { return ref.get(); } }; } public ReferenceKey createKey(ReferenceQueue queue, K key) { return new WeakReferenceKey(queue, key); } } private class SoftReferenceFactory extends ReferenceFactory { private class SoftReferenceKey extends ReferenceKey { private final SoftReference ref; public SoftReferenceKey(final ReferenceQueue queue, final K key) { super(key); this.ref = new SoftReference(key, queue) { public void clear() { remove(); super.clear(); } }; } public K get() { return this.ref.get(); } } public ReferenceValue createValue(final ReferenceQueue queue, final V value) { return new ReferenceValue() { private final SoftReference ref = new SoftReference(value, queue); public V get() { return ref.get(); } }; } public ReferenceKey createKey(final ReferenceQueue queue, final K key) { return new SoftReferenceKey(queue, key); } } public abstract class ReferenceKey { private final int hashCode; public ReferenceKey(K key) { this.hashCode = key.hashCode(); } protected abstract K get(); public boolean equals(Object obj) { if (this == obj) return true; K me = this.get(); if (me != null) { if (obj == me) return true; if (obj instanceof ReferenceKey) { K them = ((ReferenceKey) obj).get(); return me == them || me.equals(them); } } return false; } public void remove() { cache.remove(this); } public int hashCode() { return this.hashCode; } } public interface ReferenceValue { public V get(); } private class ReferenceQueueRunner extends ReferenceQueue implements Runnable { public void run() { while (true) { try { Reference ref = this.remove(); if (ref != null) { ref.clear(); } } catch (InterruptedException e) { break; //e.printStackTrace(); } } } } private final ConcurrentMap,ReferenceValue> cache; private final ReferenceFactory keyFactory; private final ReferenceFactory valueFactory; private final ReferenceFactory lookupFactory; private final ReferenceQueueRunner queue; private Thread queueMonitor; public static enum Type { Strong, Weak, Soft }; /** * Creates a new instance of ReferenceMap */ public ReferenceCache(Type keyType, Type valueType) { this(keyType, valueType, 0); } public ReferenceCache(Type keyType, Type valueType, int initialSize) { this.keyFactory = toFactory(keyType); this.valueFactory = toFactory(valueType); this.lookupFactory = new StrongReferenceFactory(); this.cache = new ConcurrentHashMap, ReferenceValue>(initialSize); this.queue = new ReferenceQueueRunner(); } public void startMonitor() { if (queueMonitor == null) { queueMonitor = new Thread(this.queue); queueMonitor.setName("jboss EL reference queue cleanup thread"); queueMonitor.setDaemon(true); queueMonitor.start(); } } public void stopMonitor() { if (queueMonitor!=null) { queueMonitor.interrupt(); queueMonitor = null; } } private final ReferenceFactory toFactory(Type type) { switch (type) { case Strong : return new StrongReferenceFactory(); case Weak : return new WeakReferenceFactory(); case Soft : return new SoftReferenceFactory(); default : throw new IllegalArgumentException("Invalid ReferenceType: " + type); } } protected abstract V create(K key); public V get(final Object key) { try { ReferenceKey refKey = this.lookupFactory.createKey(this.queue, (K) key); ReferenceValue refVal = this.cache.get(refKey); V value = dereferenceValue(refVal); if (value != null) { return value; } else { V created = create((K) key); refVal = valueFactory.createValue(queue, created); refKey = this.keyFactory.createKey(this.queue, (K) key); refVal = this.cache.putIfAbsent(refKey, refVal); value = dereferenceValue(refVal); if (value == null) { value = this.create((K) key); this.put((K) key, value); } return value; } } catch (Exception e) { if (e instanceof RuntimeException) throw (RuntimeException) e; throw new IllegalStateException(e); } } private V dereferenceValue(ReferenceValue refValue) { return refValue == null ? null : refValue.get(); } public V put(K key, final V value) { ReferenceKey refKey = this.keyFactory.createKey(this.queue, key); ReferenceValue refVal = valueFactory.createValue(queue, value); refVal = this.cache.putIfAbsent(refKey, refVal); return value; } public V remove(Object key) { ReferenceKey keyRef = this.lookupFactory.createKey(this.queue, key); return this.dereferenceValue(this.cache.remove(keyRef)); } public int size() { return this.cache.size(); } public void clear() { this.cache.clear(); } }