/** * Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.sesame.cache; import java.util.Collection; import java.util.Iterator; import java.util.List; import javax.inject.Provider; import org.threeten.bp.ZonedDateTime; import com.google.common.cache.Cache; import com.google.common.collect.HashMultimap; import com.google.common.collect.Lists; import com.google.common.collect.SetMultimap; import com.opengamma.id.ExternalId; import com.opengamma.id.ExternalIdBundle; import com.opengamma.id.ObjectId; import com.opengamma.id.VersionCorrection; import com.opengamma.sesame.marketdata.MarketDataId; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.tuple.Pair; import com.opengamma.util.tuple.Pairs; /** * TODO if this turns out to be a point of contention will need to remove the locking and make thread safe * or have multiple thread local copies and merge them at the end of the cycle before the invalidation step * TODO review which of the register methods are still needed with MarketDataBundle */ public class DefaultCacheInvalidator implements CacheInvalidator { private final Provider<Collection<MethodInvocationKey>> _executingMethods; private final SetMultimap<ObjectId, MethodInvocationKey> _objectIdsToKeys = HashMultimap.create(); private final SetMultimap<ExternalId, MethodInvocationKey> _externalIdsToKeys = HashMultimap.create(); private final List<Pair<MethodInvocationKey, ValuationTimeCacheEntry>> _valuationTimeEntries = Lists.newArrayList(); private final Cache<Object, Object> _cache; private VersionCorrection _configVersionCorrection; /** * @param executingMethods provides the keys representing the cacheable methods that are currently executing * @param cache the cache whose entries should be invalidated when data changes */ public DefaultCacheInvalidator(Provider<Collection<MethodInvocationKey>> executingMethods, Cache<Object, Object> cache) { _cache = ArgumentChecker.notNull(cache, "cache"); _executingMethods = ArgumentChecker.notNull(executingMethods, "executingMethods"); } @Override public synchronized void register(ExternalId id) { _externalIdsToKeys.putAll(id, _executingMethods.get()); } @Override public synchronized void register(ExternalIdBundle bundle) { for (ExternalId id : bundle.getExternalIds()) { register(id); } } @Override public synchronized void register(ObjectId id) { if (VersionCorrection.LATEST.equals(_configVersionCorrection)) { _objectIdsToKeys.putAll(id, _executingMethods.get()); } } @Override public synchronized void register(ValuationTimeCacheEntry entry) { for (MethodInvocationKey key : _executingMethods.get()) { _valuationTimeEntries.add(Pairs.of(key, entry)); } } @Override public void register(MarketDataId marketDataId) { throw new UnsupportedOperationException(); } @Override public synchronized void invalidate(ZonedDateTime valuationTime, VersionCorrection configVersionCorrection, // TODO should this be Collection<MarketDataRequirement>? Collection<ExternalId> marketData, Collection<ObjectId> dbIds) { ArgumentChecker.notNull(valuationTime, "valuationTime"); ArgumentChecker.notNull(configVersionCorrection, "configVersionCorrection"); ArgumentChecker.notNull(marketData, "marketData"); ArgumentChecker.notNull(dbIds, "dbIds"); invalidateValuationTime(valuationTime); // TODO if the new VC isn't the same as the old then clear all DB dependent entries _configVersionCorrection = configVersionCorrection; for (ExternalId externalId : marketData) { _cache.invalidateAll(_externalIdsToKeys.removeAll(externalId)); } for (ObjectId objectId : dbIds) { _cache.invalidateAll(_objectIdsToKeys.removeAll(objectId)); } } private void invalidateValuationTime(ZonedDateTime valuationTime) { for (Iterator<Pair<MethodInvocationKey, ValuationTimeCacheEntry>> itr = _valuationTimeEntries.iterator(); itr.hasNext(); ) { Pair<MethodInvocationKey, ValuationTimeCacheEntry> pair = itr.next(); MethodInvocationKey key = pair.getFirst(); ValuationTimeCacheEntry timeEntry = pair.getSecond(); if (!timeEntry.isValidAt(valuationTime)) { _cache.invalidate(key); itr.remove(); } } } }