/** * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.engine.cache; import java.util.Collection; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.locks.ReentrantLock; import org.fudgemsg.FudgeContext; import org.fudgemsg.FudgeMsg; import com.opengamma.engine.value.ValueSpecification; import com.opengamma.id.UniqueId; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.tuple.Pair; /** * A {@link ViewComputationCacheSource} implementation based on {@link DefaultViewComputationCache} with * the given {@link IdentifierMap} and {@link FudgeMessageStore} objects supplied by the given * {@link FudgeMessageStoreFactory} instance. */ public class DefaultViewComputationCacheSource implements ViewComputationCacheSource { /** * Callback to receive notification when a computation cycle's caches are released. */ public interface ReleaseCachesCallback { void onReleaseCaches(UniqueId viewCycleId); } /** * Callback to locate missing data. */ public interface MissingValueLoader { FudgeMsg findMissingValue(ViewComputationCacheKey cache, long identifier); Map<Long, FudgeMsg> findMissingValues(ViewComputationCacheKey cache, Collection<Long> identifier); } private final IdentifierMap _identifierMap; private final FudgeContext _fudgeContext; private final ConcurrentMap<ViewComputationCacheKey, DefaultViewComputationCache> _cachesByKey = new ConcurrentHashMap<ViewComputationCacheKey, DefaultViewComputationCache>(); private final Map<UniqueId, List<ViewComputationCacheKey>> _activeCachesByCycle = new HashMap<UniqueId, List<ViewComputationCacheKey>>(); private final ReentrantLock _cacheManagementLock = new ReentrantLock(); private final FudgeMessageStoreFactory _privateDataStoreFactory; private final FudgeMessageStoreFactory _sharedDataStoreFactory; private ReleaseCachesCallback _releaseCachesCallback; private MissingValueLoader _missingValueLoader; protected DefaultViewComputationCacheSource(final IdentifierMap identifierMap, final FudgeContext fudgeContext, final FudgeMessageStoreFactory dataStoreFactory) { this(identifierMap, fudgeContext, dataStoreFactory, dataStoreFactory); } public DefaultViewComputationCacheSource(final IdentifierMap identifierMap, final FudgeContext fudgeContext, final FudgeMessageStoreFactory privateDataStoreFactory, final FudgeMessageStoreFactory sharedDataStoreFactory) { ArgumentChecker.notNull(identifierMap, "Identifier map"); ArgumentChecker.notNull(fudgeContext, "Fudge context"); ArgumentChecker.notNull(privateDataStoreFactory, "Private data store factory"); ArgumentChecker.notNull(sharedDataStoreFactory, "Shared data store factory"); _identifierMap = identifierMap; _fudgeContext = fudgeContext; _privateDataStoreFactory = privateDataStoreFactory; _sharedDataStoreFactory = sharedDataStoreFactory; } /** * Gets the identifierMap field. * @return the identifierMap */ public IdentifierMap getIdentifierMap() { return _identifierMap; } /** * Gets the fudgeContext field. * @return the fudgeContext */ public FudgeContext getFudgeContext() { return _fudgeContext; } @Override public ViewComputationCache cloneCache(UniqueId viewCycleId, String calculationConfigurationName) { final ViewComputationCacheKey key = new ViewComputationCacheKey(viewCycleId, calculationConfigurationName); final DefaultViewComputationCache cache = _cachesByKey.get(key); final InMemoryIdentifierMap identifierMap = new InMemoryIdentifierMap(); final FudgeMessageStore dataStore = new DefaultFudgeMessageStore(new InMemoryBinaryDataStore(), getFudgeContext()); for (Pair<ValueSpecification, FudgeMsg> value : cache) { dataStore.put(identifierMap.getIdentifier(value.getFirst()), value.getSecond()); } return new DefaultViewComputationCache(identifierMap, dataStore, dataStore, getFudgeContext()); } @Override public DefaultViewComputationCache getCache(UniqueId viewCycleId, String calculationConfigurationName) { return getCache(new ViewComputationCacheKey(viewCycleId, calculationConfigurationName)); } public DefaultViewComputationCache getCache(final ViewComputationCacheKey key) { DefaultViewComputationCache cache = findCache(key); if (cache == null) { cache = constructCache(key); } return cache; } protected DefaultViewComputationCache findCache(UniqueId viewCycleId, String calculationConfigurationName) { return findCache(new ViewComputationCacheKey(viewCycleId, calculationConfigurationName)); } protected DefaultViewComputationCache findCache(final ViewComputationCacheKey key) { return _cachesByKey.get(key); } protected DefaultViewComputationCache constructCache(final ViewComputationCacheKey key) { DefaultViewComputationCache cache = null; _cacheManagementLock.lock(); try { // Have to double-check. Too expensive to construct otherwise. cache = findCache(key); if (cache == null) { final FudgeMessageStore privateDataStore = _privateDataStoreFactory.createMessageStore(key); final FudgeMessageStore sharedDataStore = (_privateDataStoreFactory == _sharedDataStoreFactory) ? privateDataStore : _sharedDataStoreFactory.createMessageStore(key); cache = createViewComputationCache(getIdentifierMap(), privateDataStore, sharedDataStore, getFudgeContext()); _cachesByKey.put(key, cache); List<ViewComputationCacheKey> caches = _activeCachesByCycle.get(key.getViewCycleId()); if (caches == null) { caches = new LinkedList<ViewComputationCacheKey>(); _activeCachesByCycle.put(key.getViewCycleId(), caches); } caches.add(key); final MissingValueLoader loader = getMissingValueLoader(); if (loader != null) { cache.setMissingValueLoader(new DefaultViewComputationCache.MissingValueLoader() { @Override public FudgeMsg findMissingValue(final long identifier) { return loader.findMissingValue(key, identifier); } @Override public Map<Long, FudgeMsg> findMissingValues(Collection<Long> identifiers) { return loader.findMissingValues(key, identifiers); } }); } } } finally { _cacheManagementLock.unlock(); } return cache; } /** * Override this method if you need to create a different sub-class of {@link DefaultViewComputationCache}. * * @param identifierMap the identifier map * @param privateDataStore the message store for private values * @param sharedDataStore the message store for shared values * @param fudgeContext the Fudge context * @return a new {@link DefaultViewComputationCache} instance */ protected DefaultViewComputationCache createViewComputationCache(final IdentifierMap identifierMap, final FudgeMessageStore privateDataStore, final FudgeMessageStore sharedDataStore, final FudgeContext fudgeContext) { return new DefaultViewComputationCache(identifierMap, privateDataStore, sharedDataStore, fudgeContext); } @Override public void releaseCaches(UniqueId viewCycleId) { ArgumentChecker.notNull(viewCycleId, "viewCycleId"); final ReleaseCachesCallback callback = getReleaseCachesCallback(); if (callback != null) { callback.onReleaseCaches(viewCycleId); } DefaultViewComputationCache[] caches; _cacheManagementLock.lock(); try { final List<ViewComputationCacheKey> cacheKeys = _activeCachesByCycle.remove(viewCycleId); if (cacheKeys == null) { return; } caches = new DefaultViewComputationCache[cacheKeys.size()]; int i = 0; for (ViewComputationCacheKey key : cacheKeys) { caches[i++] = _cachesByKey.remove(key); } } finally { _cacheManagementLock.unlock(); } for (DefaultViewComputationCache cache : caches) { cache.delete(); } } public void setReleaseCachesCallback(final ReleaseCachesCallback releaseCachesCallback) { _releaseCachesCallback = releaseCachesCallback; } public ReleaseCachesCallback getReleaseCachesCallback() { return _releaseCachesCallback; } public void setMissingValueLoader(final MissingValueLoader missingValueLoader) { _missingValueLoader = missingValueLoader; } public MissingValueLoader getMissingValueLoader() { return _missingValueLoader; } }