/**
* Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.financial.convention;
import java.util.concurrent.ConcurrentMap;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import com.google.common.collect.MapMaker;
import com.opengamma.id.ExternalId;
import com.opengamma.id.ExternalIdBundle;
import com.opengamma.id.UniqueId;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.ehcache.EHCacheUtils;
/**
* A cached form of {@link ConventionBundleSource}.
*/
public class EHCachingConventionBundleSource implements ConventionBundleSource {
private static final String CONVENTION_CACHE_NAME = "conventionBundle";
private final ConventionBundleSource _underlying;
private final CacheManager _cacheManager;
private final Cache _conventionCache;
private final ConcurrentMap<Object, ConventionBundle> _frontCache = new MapMaker().weakValues().makeMap();
public EHCachingConventionBundleSource(final ConventionBundleSource underlying, final CacheManager cacheManager) {
ArgumentChecker.notNull(underlying, "underlying");
ArgumentChecker.notNull(cacheManager, "cacheManager");
_underlying = underlying;
_cacheManager = cacheManager;
EHCacheUtils.addCache(cacheManager, CONVENTION_CACHE_NAME);
_conventionCache = EHCacheUtils.getCacheFromManager(cacheManager, CONVENTION_CACHE_NAME);
}
protected ConventionBundleSource getUnderlying() {
return _underlying;
}
protected CacheManager getCacheManager() {
return _cacheManager;
}
/**
* Call this at the end of a unit test run to clear the state of EHCache. It should not be part of a generic lifecycle method.
*/
protected void shutdown() {
getCacheManager().removeCache(CONVENTION_CACHE_NAME);
}
/**
* For use by test methods only to control the front cache.
*/
/* package */void emptyFrontCache() {
_frontCache.clear();
}
/**
* For use by test methods only to control the EH cache.
*/
/* package */void emptyEHCache() {
EHCacheUtils.clear(getCacheManager(), CONVENTION_CACHE_NAME);
}
protected ConventionBundle frontCache(final ConventionBundle bundle) {
final ConventionBundle existing = _frontCache.putIfAbsent(bundle.getUniqueId(), bundle);
if (existing == null) {
if (bundle.getIdentifiers() != null) {
_frontCache.put(bundle.getIdentifiers(), bundle);
}
return bundle;
}
return existing;
}
// ConventionBundleSource
@Override
public ConventionBundle getConventionBundle(final ExternalId identifier) {
ConventionBundle bundle = _frontCache.get(identifier);
if (bundle != null) {
return bundle;
}
final Element e = _conventionCache.get(identifier);
if (e != null) {
bundle = frontCache((ConventionBundle) e.getObjectValue());
_frontCache.put(identifier, bundle);
return bundle;
}
bundle = getUnderlying().getConventionBundle(identifier);
if (bundle == null) {
// TODO: Is it worth caching the misses?
return null;
}
bundle = frontCache(bundle);
_conventionCache.put(new Element(identifier, bundle));
_frontCache.put(identifier, bundle);
return bundle;
}
@Override
public ConventionBundle getConventionBundle(final ExternalIdBundle identifiers) {
ConventionBundle bundle = _frontCache.get(identifiers);
if (bundle != null) {
return bundle;
}
final Element e = _conventionCache.get(identifiers);
if (e != null) {
bundle = frontCache((ConventionBundle) e.getObjectValue());
_frontCache.put(identifiers, bundle);
return bundle;
}
bundle = getUnderlying().getConventionBundle(identifiers);
if (bundle == null) {
// TODO: is it worth caching the misses?
return null;
}
bundle = frontCache(bundle);
_conventionCache.put(new Element(identifiers, bundle));
_frontCache.put(identifiers, bundle);
return bundle;
}
@Override
public ConventionBundle getConventionBundle(final UniqueId identifier) {
ConventionBundle bundle = _frontCache.get(identifier);
if (bundle != null) {
return bundle;
}
final Element e = _conventionCache.get(identifier);
if (e != null) {
return frontCache((ConventionBundle) e.getObjectValue());
}
bundle = getUnderlying().getConventionBundle(identifier);
if (bundle == null) {
// TODO: is it worth caching the misses?
return null;
}
bundle = frontCache(bundle);
_conventionCache.put(new Element(identifier, bundle));
return bundle;
}
}