/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.bbg.referencedata.cache;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Map;
import java.util.Set;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import org.fudgemsg.FudgeContext;
import org.fudgemsg.FudgeMsg;
import org.fudgemsg.mapping.FudgeSerializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.Maps;
import com.opengamma.bbg.referencedata.ReferenceData;
import com.opengamma.bbg.referencedata.ReferenceDataProvider;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.fudgemsg.OpenGammaFudgeContext;
/**
* Decorates a reference data provider, adding caching.
* <p>
* The cache is implemented using {@code EHCache}.
*/
public class EHValueCachingReferenceDataProvider extends AbstractValueCachingReferenceDataProvider {
/** Logger. */
private static final Logger s_logger = LoggerFactory.getLogger(EHValueCachingReferenceDataProvider.class);
/**
* Cache key for reference data.
*/
/*package*/ static final String REFERENCE_DATA_CACHE = "referenceData";
/**
* The cache manager.
*/
private final CacheManager _cacheManager;
/**
* The reference data cache.
*/
private final Cache _cache;
/**
* Creates an instance.
*
* @param underlying the underlying reference data provider, not null
* @param cacheManager the cache manager, not null
*/
public EHValueCachingReferenceDataProvider(final ReferenceDataProvider underlying, final CacheManager cacheManager) {
this(underlying, cacheManager, OpenGammaFudgeContext.getInstance());
}
/**
* Creates an instance.
*
* @param underlying the underlying reference data provider, not null
* @param cacheManager the cache manager, not null
* @param fudgeContext the Fudge context, not null
*/
public EHValueCachingReferenceDataProvider(final ReferenceDataProvider underlying, final CacheManager cacheManager, final FudgeContext fudgeContext) {
super(underlying, fudgeContext);
ArgumentChecker.notNull(cacheManager, "cacheManager");
_cacheManager = cacheManager;
_cacheManager.addCacheIfAbsent(REFERENCE_DATA_CACHE);
_cache = _cacheManager.getCache(REFERENCE_DATA_CACHE);
}
//-------------------------------------------------------------------------
/**
* Gets the cache manager.
*
* @return the cache manager, not null
*/
public CacheManager getCacheManager() {
return _cacheManager;
}
//-------------------------------------------------------------------------
@Override
protected Map<String, ReferenceData> loadFieldValues(Set<String> identifiers) {
Map<String, ReferenceData> result = Maps.newTreeMap();
FudgeSerializer serializer = new FudgeSerializer(getFudgeContext());
for (String identifier : identifiers) {
ReferenceData cachedResult = loadStateFromCache(serializer, identifier);
if (cachedResult != null) {
result.put(identifier, cachedResult);
}
}
return result;
}
@Override
protected void saveFieldValues(ReferenceData result) {
String identifier = result.getIdentifier();
FudgeMsg fieldData = result.getFieldValues();
if (identifier != null && fieldData != null) {
s_logger.info("Persisting fields for \"{}\": {}", identifier, result.getFieldValues());
Object cachedObject = createCachedObject(result);
s_logger.debug("cachedObject={}", cachedObject);
Element element = new Element(identifier, cachedObject);
_cache.put(element);
}
}
//-------------------------------------------------------------------------
/**
* Loads the state from the cache.
*
* @param serializer the Fudge serializer, not null
* @param identifier the identifier, not null
* @return the result, null if not found
*/
protected ReferenceData loadStateFromCache(FudgeSerializer serializer, String identifier) {
Element element = _cache.get(identifier);
if (element != null) {
s_logger.debug("Have security data for des {} in cache", identifier);
Object fromCache = element.getObjectValue();
s_logger.debug("cachedObject={}", fromCache);
return parseCachedObject(fromCache);
}
return null;
}
//-------------------------------------------------------------------------
/**
* Data holder for storing the results.
*/
private static final class CachedReferenceData implements Serializable {
private static final long serialVersionUID = 3L;
private transient String _security;
private transient FudgeContext _fudgeContext;
private transient volatile FudgeMsg _fieldDataMsg;
private transient volatile byte[] _fieldData;
private byte[] getFieldData() {
byte[] fieldData = _fieldData;
if (fieldData == null) {
synchronized (this) {
fieldData = _fieldData;
if (fieldData == null) {
fieldData = _fudgeContext.toByteArray(_fieldDataMsg);
_fieldData = fieldData;
_fieldDataMsg = null;
}
}
}
return fieldData;
}
private void setFieldData(final byte[] fieldData) {
_fieldData = fieldData;
}
public FudgeMsg getFieldDataMsg(final FudgeContext fudgeContext) {
FudgeMsg fieldDataMsg = _fieldDataMsg;
if (fieldDataMsg == null) {
synchronized (this) {
fieldDataMsg = _fieldDataMsg;
if (fieldDataMsg == null) {
_fudgeContext = fudgeContext;
fieldDataMsg = fudgeContext.deserialize(_fieldData).getMessage();
_fieldDataMsg = fieldDataMsg;
_fieldData = null;
}
}
}
return _fieldDataMsg;
}
public void setFieldDataMsg(final FudgeMsg fieldDataMsg, final FudgeContext fudgeContext) {
_fieldDataMsg = fieldDataMsg;
_fudgeContext = fudgeContext;
}
public String getSecurity() {
return _security;
}
public void setSecurity(final String security) {
_security = security;
}
private void writeObject(final ObjectOutputStream out) throws IOException {
out.writeUTF(getSecurity());
final byte[] fieldData = getFieldData();
out.writeInt(fieldData.length);
out.write(fieldData);
}
private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
setSecurity(in.readUTF());
final int dataLength = in.readInt();
final byte[] data = new byte[dataLength];
in.readFully(data);
setFieldData(data);
}
}
//-------------------------------------------------------------------------
/**
* Parse the cached object.
*
* @param fromCache the data from the cache, not null
* @return the result, not null
*/
protected ReferenceData parseCachedObject(Object fromCache) {
CachedReferenceData rd = (CachedReferenceData) fromCache;
return new ReferenceData(rd.getSecurity(), rd.getFieldDataMsg(getFudgeContext()));
}
/**
* Creates the cached object.
*
* @param refDataResult the reference data result.
* @return the cache object, not null
*/
protected Object createCachedObject(ReferenceData refDataResult) {
CachedReferenceData result = new CachedReferenceData();
result.setSecurity(refDataResult.getIdentifier());
result.setFieldDataMsg(getFudgeContext().newMessage(refDataResult.getFieldValues()), getFudgeContext());
return result;
}
}