/**
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.engine.cache;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import com.opengamma.id.UniqueId;
import com.opengamma.util.ArgumentChecker;
/**
* A factory for {@link MapBackedInMemoryViewComputationCache}.
* It fully supports cache releasing for garbage collection.
*/
public class MapBackedInMemoryViewComputationCacheSource implements ViewComputationCacheSource {
/**
* All current caches. This is not a concurrent map as access to it is controlled by
* a separate lock.
*/
private final Map<ViewComputationCacheKey, MapBackedInMemoryViewComputationCache> _currentCaches =
new HashMap<ViewComputationCacheKey, MapBackedInMemoryViewComputationCache>();
/**
* Controls access to the cache.
* We do it this way because the initial size of the underlying map in
* MapBackedInMemoryViewComputationCache is very large, so it's an expensive object
* to construct. We don't want to use putIfAbsent in this case.
*/
private final ReadWriteLock _cacheLock = new ReentrantReadWriteLock();
@Override
public ViewComputationCache getCache(UniqueId viewCycleId, String calculationConfigurationName) {
ViewComputationCacheKey key = new ViewComputationCacheKey(viewCycleId, calculationConfigurationName);
MapBackedInMemoryViewComputationCache cache = null;
_cacheLock.readLock().lock();
try {
cache = _currentCaches.get(key);
if (cache != null) {
return cache;
}
} finally {
_cacheLock.readLock().unlock();
}
_cacheLock.writeLock().lock();
try {
cache = _currentCaches.get(key);
if (cache == null) {
cache = new MapBackedInMemoryViewComputationCache();
_currentCaches.put(key, cache);
}
return cache;
} finally {
_cacheLock.writeLock().unlock();
}
}
@Override
public ViewComputationCache cloneCache(UniqueId viewCycleId, String calculationConfigurationName) {
ViewComputationCacheKey key = new ViewComputationCacheKey(viewCycleId, calculationConfigurationName);
_cacheLock.readLock().lock();
try {
MapBackedInMemoryViewComputationCache cache = _currentCaches.get(key);
if (cache == null) {
return null;
}
return new MapBackedInMemoryViewComputationCache(cache);
} finally {
_cacheLock.readLock().unlock();
}
}
@Override
public void releaseCaches(UniqueId viewCycleId) {
ArgumentChecker.notNull(viewCycleId, "viewCycleId");
_cacheLock.writeLock().lock();
try {
Iterator<Map.Entry<ViewComputationCacheKey, MapBackedInMemoryViewComputationCache>> iterator = _currentCaches.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<ViewComputationCacheKey, MapBackedInMemoryViewComputationCache> entry = iterator.next();
if (entry.getKey().getViewCycleId().equals(viewCycleId)) {
// This is just to give a little extra help to the GC in the event that it doesn't
// clear up the parent object.
entry.getValue().clear();
iterator.remove();
}
}
} finally {
_cacheLock.writeLock().unlock();
}
}
}