package pt.ist.fenixframework.pstm; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import pt.ist.fenixframework.DomainObject; public class FenixCache { private static final ReferenceQueue<DomainObject> refQueue = new ReferenceQueue<DomainObject>(); private ConcurrentHashMap<Long,CacheEntry> cache; public FenixCache() { this.cache = new ConcurrentHashMap<Long,CacheEntry>(); } public DomainObject cache(DomainObject obj) { processQueue(); Long key = obj.getOid(); CacheEntry newEntry = new CacheEntry(obj, key, this.refQueue); return cacheNewEntry(newEntry, obj); } private DomainObject cacheNewEntry(CacheEntry newEntry, DomainObject obj) { CacheEntry entryInCache = putIfAbsent(this.cache, newEntry.key, newEntry); if (entryInCache == newEntry) { return obj; } else { DomainObject objInCache = entryInCache.get(); if (objInCache != null) { return objInCache; } else { // the entry in cache was GCed already, so remove it and retry removeEntry(entryInCache); return cacheNewEntry(newEntry, obj); } } } public DomainObject lookup(Long key) { processQueue(); CacheEntry entry = this.cache.get(key); if (entry != null) { DomainObject result = entry.get(); if (result != null) { return result; } else { removeEntry(entry); return null; } } else { return null; } } private void removeEntry(CacheEntry entry) { this.cache.remove(entry.key, entry); } /* This method stores the new value if an older one didn't exist already. In either case it returns the value that was left * in the cache. This implementation works as intended because we assume that we never store a null value in the * cache. Otherwise, map.putIfAbsent would not put a new value over a null entry. * * This method's behaviour is very important to ensure that we do not inadvertently permit more than one reference to the same * domain object to wander around in the system. */ private static <K,V> V putIfAbsent(ConcurrentHashMap<K,V> map, K key, V value) { V oldValue = map.putIfAbsent(key, value); return ((oldValue == null) ? value : oldValue); } private void processQueue() { CacheEntry gcedEntry = (CacheEntry)refQueue.poll(); while (gcedEntry != null) { removeEntry(gcedEntry); gcedEntry = (CacheEntry)refQueue.poll(); } } private static class CacheEntry extends SoftReference<DomainObject> { private final Long key; CacheEntry(DomainObject object, Long key, ReferenceQueue q) { super(object, q); this.key = key; } } }