/** * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.core.historicaltimeseries.impl; import java.util.HashMap; import java.util.Map; import net.sf.ehcache.Cache; import net.sf.ehcache.CacheManager; import net.sf.ehcache.Element; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.opengamma.util.ehcache.EHCacheUtils; import com.opengamma.util.function.Supplier; /** * Abstract cache. * * @param <A> the first type * @param <B> the second type */ public abstract class HierarhicalEHCache<A, B> { private final Cache _aCache; private final Cache _bCache; private final Cache _missedCache; private final String _cacheNameA = getCachePrefix() + "A_Cache"; private final String _cacheNameB = getCachePrefix() + "B_Cache"; private final String _missedCacheName = getCachePrefix() + "Missed_Cache"; abstract String getCachePrefix(); abstract Object extractKey(A key, B value); private long _timeout = 1000; /** Logger. */ private static final Logger s_logger = LoggerFactory.getLogger(HierarhicalEHCache.class); public HierarhicalEHCache(CacheManager cacheManager) { EHCacheUtils.addCache(cacheManager, _cacheNameA); _aCache = EHCacheUtils.getCacheFromManager(cacheManager, _cacheNameA); EHCacheUtils.addCache(cacheManager, _cacheNameB); _bCache = EHCacheUtils.getCacheFromManager(cacheManager, _cacheNameB); EHCacheUtils.addCache(cacheManager, _missedCacheName); _missedCache = EHCacheUtils.getCacheFromManager(cacheManager, _missedCacheName); } public void setTimeout(long timeout) { _timeout = timeout; } public void markMissed(Object key) { _missedCache.put(new Element(key, null)); } @SuppressWarnings("unchecked") public B deepInsert(A aKey, Object bKey, B value) { try { _bCache.tryWriteLockOnKey(bKey, _timeout); // reread the cached element Element bElement = _bCache.get(bKey); Map<Object, B> map; if (bElement != null) { map = (Map<Object, B>) bElement.getObjectValue(); } else { map = new HashMap<>(); } s_logger.debug(getCachePrefix() + ": Caching value {}", value); map.put(aKey, value); // reinsert the map into cache _bCache.put(new Element(bKey, map)); } catch (InterruptedException e) { // interrupted so we will not store value in cache this time } finally { _bCache.releaseWriteLockOnKey(bKey); } return value; } private B deepInsertAndMarkMissed(A aKey, Supplier<B> closure) { if (closure == null) { return null; } B b = closure.get(); if (b == null) { _missedCache.put(new Element(aKey, null)); return null; } else { Object bKey = extractKey(aKey, b); deepInsert(aKey, bKey, b); _aCache.put(new Element(aKey, bKey)); return b; } } @SuppressWarnings("unchecked") public B shallowInsert(Object bKey, B value) { try { _bCache.tryWriteLockOnKey(bKey, _timeout); // reread the cached element Element bElement = _bCache.get(bKey); Map<Object, B> map; if (bElement != null) { map = (Map<Object, B>) bElement.getObjectValue(); } else { map = new HashMap<>(); } s_logger.debug(getCachePrefix() + ": Caching value {}", value); map.put(bKey, value); // reinsert the map into cache _bCache.put(new Element(bKey, map)); } catch (InterruptedException e) { // interrupted so we will not store value in cache this time } finally { _bCache.releaseWriteLockOnKey(bKey); } return value; } private B shallowInsertAndMarkMissed(Object bKey, Supplier<B> closure) { if (closure == null) { return null; } B b = closure.get(); if (b == null) { _missedCache.put(new Element(bKey, null)); return null; } else { shallowInsert(bKey, b); return b; } } public B get(A aKey, Supplier<B> closure) { if (_missedCache.isKeyInCache(aKey)) { s_logger.debug(getCachePrefix() + ": Caching miss on {}", aKey); return null; } Element aElement = _aCache.get(aKey); if (aElement != null) { Object bKey = aElement.getObjectValue(); Element bElement = _bCache.get(bKey); if (bElement != null) { @SuppressWarnings("unchecked") Map<Object, B> map = (Map<Object, B>) bElement.getObjectValue(); B value = map.get(aKey); if (value == null) { return deepInsertAndMarkMissed(aKey, closure); } else { return value; } } } return deepInsertAndMarkMissed(aKey, closure); } public B getBySecondKey(Object bKey, Supplier<B> closure) { if (_missedCache.isKeyInCache(bKey)) { s_logger.debug(getCachePrefix() + ": Caching miss on {}", bKey); return null; } Element bElement = _bCache.get(bKey); if (bElement != null) { @SuppressWarnings("unchecked") Map<Object, B> map = (Map<Object, B>) bElement.getObjectValue(); B value = map.get(bKey); if (value == null) { return shallowInsertAndMarkMissed(bKey, closure); } else { return value; } } return shallowInsertAndMarkMissed(bKey, closure); } public void clear(Object bKey) { _bCache.remove(bKey); _missedCache.removeAll(); } public void shutdown() { _aCache.getCacheManager().removeCache(_cacheNameA); _bCache.getCacheManager().removeCache(_cacheNameB); _missedCache.getCacheManager().removeCache(_missedCacheName); } }