Package com.opengamma.engine.cache

Source Code of com.opengamma.engine.cache.DefaultViewComputationCache$MissingValueLoader

/**
* 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.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.fudgemsg.FudgeContext;
import org.fudgemsg.FudgeFieldType;
import org.fudgemsg.FudgeMsg;
import org.fudgemsg.FudgeRuntimeException;
import org.fudgemsg.MutableFudgeMsg;
import org.fudgemsg.mapping.FudgeDeserializer;
import org.fudgemsg.mapping.FudgeSerializer;
import org.fudgemsg.wire.FudgeSize;
import org.fudgemsg.wire.types.FudgeWireType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.Lists;
import com.opengamma.engine.value.ComputedValue;
import com.opengamma.engine.value.ValueSpecification;
import com.opengamma.util.tuple.Pair;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.fudgemsg.WriteReplaceHelper;

/**
* An implementation of {@link ViewComputationCache} which backs value storage on a pair of {@link IdentifierMap} and {@link FudgeMessageStore}.
*/
public class DefaultViewComputationCache implements ViewComputationCache,
    Iterable<Pair<ValueSpecification, FudgeMsg>> {
 
  private static final Logger s_logger = LoggerFactory.getLogger(DefaultViewComputationCache.class);

  /**
   * Callback to locate missing data.
   */
  public interface MissingValueLoader {

    FudgeMsg findMissingValue(long identifier);

    Map<Long, FudgeMsg> findMissingValues(Collection<Long> identifiers);

  };

  private static final int NATIVE_FIELD_INDEX = -1;

  private final IdentifierMap _identifierMap;
  private final FudgeMessageStore _privateDataStore;
  private final FudgeMessageStore _sharedDataStore;
  private final FudgeContext _fudgeContext;

  private MissingValueLoader _missingValueLoader;

  /**
   * The size of recent values that have gone into or come out of this cache.
   */
  @SuppressWarnings({"rawtypes", "unchecked" })
  private final ThreadLocal<Map<ValueSpecification, Integer>> _valueSizeCache = new ThreadLocal(); //NOTE: this being thread local is dangerous, but avoids blocking

  private Map<ValueSpecification, Integer> getValueSizeCache() {
    Map<ValueSpecification, Integer> c = _valueSizeCache.get();
    if (c == null) {
      c = new HashMap<ValueSpecification, Integer>();
      _valueSizeCache.set(c);
    }
    return c;
  }

  /**
   * The size of classes which will always have the same size
   */
  private final Map<Class<?>, Integer> _valueSizeByClassCache;

  private void cacheValueSize(final ValueSpecification specification, final FudgeMsg data, final Object value) {
    if (value != null && _valueSizeByClassCache.containsKey(value.getClass())) {
      return;
    }
    final int calculateMessageSize = FudgeSize.calculateMessageSize(data);
    getValueSizeCache().put(specification, calculateMessageSize);
  }

  protected DefaultViewComputationCache(final IdentifierMap identifierMap, final FudgeMessageStore dataStore,
      final FudgeContext fudgeContext) {
    this(identifierMap, dataStore, dataStore, fudgeContext);
  }

  public DefaultViewComputationCache(final IdentifierMap identifierMap, final FudgeMessageStore privateDataStore,
      final FudgeMessageStore sharedDataStore, final FudgeContext fudgeContext) {
    ArgumentChecker.notNull(identifierMap, "Identifier map");
    ArgumentChecker.notNull(privateDataStore, "Private data store");
    ArgumentChecker.notNull(sharedDataStore, "Shared data store");
    ArgumentChecker.notNull(fudgeContext, "Fudge context");
    _identifierMap = identifierMap;
    _privateDataStore = privateDataStore;
    _sharedDataStore = sharedDataStore;
    _fudgeContext = fudgeContext;
    _valueSizeByClassCache = buildValueSizeByClassMap();
  }

  private Map<Class<?>, Integer> buildValueSizeByClassMap() {
    //All of these classes must be have consistent sizes
    final ArrayList<Object> templates = Lists.<Object>newArrayList(Double.valueOf(12.0), MissingInput.MISSING_MARKET_DATA);

    final Map<Class<?>, Integer> valueSizeByClass = new HashMap<Class<?>, Integer>(templates.size());
    for (final Object obj : templates) {
      final FudgeSerializer serializer = new FudgeSerializer(getFudgeContext());
      final FudgeMsg data = serializeValue(serializer, obj);
      final int size = FudgeSize.calculateMessageSize(data);
      valueSizeByClass.put(obj.getClass(), size);
    }
    return valueSizeByClass;
  }

  public void setMissingValueLoader(final MissingValueLoader missingValueLoader) {
    _missingValueLoader = missingValueLoader;
  }

  public MissingValueLoader getMissingValueLoader() {
    return _missingValueLoader;
  }

  /**
   * Gets the identifierSource field.
   *
   * @return the identifierSource
   */
  public IdentifierMap getIdentifierMap() {
    return _identifierMap;
  }

  /**
   * Gets the private / local data store.
   *
   * @return the dataStore
   */
  public FudgeMessageStore getPrivateDataStore() {
    return _privateDataStore;
  }

  public FudgeMessageStore getSharedDataStore() {
    return _sharedDataStore;
  }

  /**
   * Gets the fudgeContext field.
   *
   * @return the fudgeContext
   */
  public FudgeContext getFudgeContext() {
    return _fudgeContext;
  }

  @Override
  public Object getValue(final ValueSpecification specification) {
    ArgumentChecker.notNull(specification, "Specification");
    final long identifier = getIdentifierMap().getIdentifier(specification);
    FudgeMsg data = getPrivateDataStore().get(identifier);
    if (data == null) {
      data = getSharedDataStore().get(identifier);
    }
    if (data == null) {
      final MissingValueLoader loader = getMissingValueLoader();
      if (loader == null) {
        return null;
      }
      data = loader.findMissingValue(identifier);
      if (data == null) {
        return null;
      }
    }
    final FudgeDeserializer deserializer = new FudgeDeserializer(getFudgeContext());
    final Object obj = deserializeValue(deserializer, data);
    cacheValueSize(specification, data, obj);
    return obj;
  }

  @Override
  public Object getValue(final ValueSpecification specification, final CacheSelectHint filter) {
    ArgumentChecker.notNull(specification, "Specification");
    final long identifier = getIdentifierMap().getIdentifier(specification);
    final boolean isPrivate = filter.isPrivateValue(specification);
    final FudgeMsg data = (isPrivate ? getPrivateDataStore() : getSharedDataStore()).get(identifier);
    if (data == null) {
      return null;
    }
    final FudgeDeserializer deserializer = new FudgeDeserializer(getFudgeContext());
    final Object obj = deserializeValue(deserializer, data);
    cacheValueSize(specification, data, obj);
    return obj;
  }

  @Override
  public Collection<Pair<ValueSpecification, Object>> getValues(final Collection<ValueSpecification> specifications) {
    ArgumentChecker.notNull(specifications, "specifications");
    final Map<ValueSpecification, Long> identifiers = getIdentifierMap().getIdentifiers(specifications);
    final Collection<Pair<ValueSpecification, Object>> returnValues = new ArrayList<Pair<ValueSpecification, Object>>(specifications.size());
    final Collection<Long> identifierValues = identifiers.values();
    final FudgeDeserializer deserializer = new FudgeDeserializer(getFudgeContext());
    Map<Long, FudgeMsg> rawValues = getPrivateDataStore().get(identifierValues);
    if (!rawValues.isEmpty()) {
      final Iterator<Map.Entry<ValueSpecification, Long>> identifierIterator = identifiers.entrySet().iterator();
      while (identifierIterator.hasNext()) {
        final Map.Entry<ValueSpecification, Long> identifier = identifierIterator.next();
        final FudgeMsg data = rawValues.get(identifier.getValue());
        if (data != null) {
          final Object value = deserializeValue(deserializer, data);
          cacheValueSize(identifier.getKey(), data, value);
          returnValues.add(Pair.of(identifier.getKey(), value));
          identifierIterator.remove();
        }
      }
      if (identifiers.isEmpty()) {
        return returnValues;
      }
    }
    rawValues = getSharedDataStore().get(identifierValues);
    if (!rawValues.isEmpty()) {
      final Iterator<Map.Entry<ValueSpecification, Long>> identifierIterator = identifiers.entrySet().iterator();
      while (identifierIterator.hasNext()) {
        final Map.Entry<ValueSpecification, Long> identifier = identifierIterator.next();
        final FudgeMsg data = rawValues.get(identifier.getValue());
        if (data != null) {
          final Object value = deserializeValue(deserializer, data);
          cacheValueSize(identifier.getKey(), data, value);
          returnValues.add(Pair.of(identifier.getKey(), value));
          identifierIterator.remove();
        }
      }
      if (identifiers.isEmpty()) {
        return returnValues;
      }
    }
    final MissingValueLoader loader = getMissingValueLoader();
    if (loader != null) {
      rawValues = loader.findMissingValues(identifierValues);
      if (!rawValues.isEmpty()) {
        final Iterator<Map.Entry<ValueSpecification, Long>> identifierIterator = identifiers.entrySet().iterator();
        while (identifierIterator.hasNext()) {
          final Map.Entry<ValueSpecification, Long> identifier = identifierIterator.next();
          final FudgeMsg data = rawValues.get(identifier.getValue());
          if (data != null) {
            final Object value = deserializeValue(deserializer, data);
            cacheValueSize(identifier.getKey(), data, value);
            returnValues.add(Pair.of(identifier.getKey(), value));
            identifierIterator.remove();
          }
        }
      }
    }
    return returnValues;
  }

  @Override
  public Collection<Pair<ValueSpecification, Object>> getValues(final Collection<ValueSpecification> specifications, final CacheSelectHint filter) {
    ArgumentChecker.notNull(specifications, "specifications");
    final Map<ValueSpecification, Long> identifiers = getIdentifierMap().getIdentifiers(specifications);
    final Collection<Pair<ValueSpecification, Object>> returnValues = new ArrayList<Pair<ValueSpecification, Object>>(specifications.size());
    List<Long> privateIdentifiers = null;
    List<Long> sharedIdentifiers = null;
    for (final ValueSpecification specification : specifications) {
      if (filter.isPrivateValue(specification)) {
        if (privateIdentifiers == null) {
          privateIdentifiers = new ArrayList<Long>(specifications.size());
        }
        privateIdentifiers.add(identifiers.get(specification));
      } else {
        if (sharedIdentifiers == null) {
          sharedIdentifiers = new ArrayList<Long>(specifications.size());
        }
        sharedIdentifiers.add(identifiers.get(specification));
      }
    }
    final Map<Long, FudgeMsg> rawValues = new HashMap<Long, FudgeMsg>();
    // TODO Can we overlay the fetch of shared and private data?
    if (sharedIdentifiers != null) {
      if (sharedIdentifiers.size() == 1) {
        final FudgeMsg data = getSharedDataStore().get(sharedIdentifiers.get(0));
        rawValues.put(sharedIdentifiers.get(0), data);
      } else {
        rawValues.putAll(getSharedDataStore().get(sharedIdentifiers));
      }
    }
    if (privateIdentifiers != null) {
      if (privateIdentifiers.size() == 1) {
        final FudgeMsg data = getPrivateDataStore().get(privateIdentifiers.get(0));
        rawValues.put(privateIdentifiers.get(0), data);
      } else {
        rawValues.putAll(getPrivateDataStore().get(privateIdentifiers));
      }
    }
    final FudgeDeserializer deserializer = new FudgeDeserializer(getFudgeContext());
    for (final Map.Entry<ValueSpecification, Long> identifier : identifiers.entrySet()) {
      final FudgeMsg data = rawValues.get(identifier.getValue());
      if (data != null) {
        final Object value = deserializeValue(deserializer, data);
        cacheValueSize(identifier.getKey(), data, value);
        returnValues.add(Pair.of(identifier.getKey(), value));
      } else {
        returnValues.add(Pair.of(identifier.getKey(), null));
      }
    }
    return returnValues;
  }

  protected void putValue(final ComputedValue value, final FudgeMessageStore dataStore) {
    ArgumentChecker.notNull(value, "value");
    final long identifier = getIdentifierMap().getIdentifier(value.getSpecification());
    final FudgeSerializer serializer = new FudgeSerializer(getFudgeContext());
    final Object obj = value.getValue();
    final FudgeMsg data = serializeValue(serializer, obj);
    cacheValueSize(value.getSpecification(), data, obj);
    dataStore.put(identifier, data);
  }

  @Override
  public void putPrivateValue(final ComputedValue value) {
    putValue(value, getPrivateDataStore());
  }

  @Override
  public void putSharedValue(final ComputedValue value) {
    putValue(value, getSharedDataStore());
  }

  @Override
  public void putValue(final ComputedValue value, final CacheSelectHint filter) {
    AbstractViewComputationCache.putValue(this, value, filter);
  }

  protected void putValues(final Collection<? extends ComputedValue> values, final FudgeMessageStore dataStore) {
    ArgumentChecker.notNull(values, "values");
    final Collection<ValueSpecification> specifications = new ArrayList<ValueSpecification>(values.size());
    for (final ComputedValue value : values) {
      specifications.add(value.getSpecification());
    }
    final Map<ValueSpecification, Long> identifiers = getIdentifierMap().getIdentifiers(specifications);
    final Map<Long, FudgeMsg> data = new HashMap<Long, FudgeMsg>();
    final FudgeSerializer serializer = new FudgeSerializer(getFudgeContext());
    for (final ComputedValue value : values) {
      final Object obj = value.getValue();
      final FudgeMsg valueData = serializeValue(serializer, obj);
      cacheValueSize(value.getSpecification(), valueData, obj);
      data.put(identifiers.get(value.getSpecification()), valueData);
    }
    dataStore.put(data);
  }

  @Override
  public void putPrivateValues(final Collection<? extends ComputedValue> values) {
    putValues(values, getPrivateDataStore());
  }

  @Override
  public void putSharedValues(final Collection<? extends ComputedValue> values) {
    putValues(values, getSharedDataStore());
  }

  @Override
  public void putValues(final Collection<? extends ComputedValue> values, final CacheSelectHint filter) {
    ArgumentChecker.notNull(values, "values");
    final Collection<ValueSpecification> specifications = new ArrayList<ValueSpecification>(values.size());
    for (final ComputedValue value : values) {
      specifications.add(value.getSpecification());
    }
    final Map<ValueSpecification, Long> identifiers = getIdentifierMap().getIdentifiers(specifications);
    final FudgeSerializer serializer = new FudgeSerializer(getFudgeContext());
    Map<Long, FudgeMsg> privateData = null;
    Map<Long, FudgeMsg> sharedData = null;
    for (final ComputedValue value : values) {
      final Object obj = value.getValue();
      final FudgeMsg valueData = serializeValue(serializer, obj);
      cacheValueSize(value.getSpecification(), valueData, value.getValue());
      if (filter.isPrivateValue(value.getSpecification())) {
        if (privateData == null) {
          privateData = new HashMap<Long, FudgeMsg>();
        }
        privateData.put(identifiers.get(value.getSpecification()), valueData);
      } else {
        if (sharedData == null) {
          sharedData = new HashMap<Long, FudgeMsg>();
        }
        sharedData.put(identifiers.get(value.getSpecification()), valueData);
      }
    }
    // TODO 2010-08-31 Andrew -- can we overlay the shared and private puts ?
    if (sharedData != null) {
      getSharedDataStore().put(sharedData);
    }
    if (privateData != null) {
      getPrivateDataStore().put(privateData);
    }
  }

  protected static FudgeMsg serializeValue(final FudgeSerializer serializer, final Object value) {
    if (value instanceof Double) {
      //Make sure fudge doesn't faff around with reflection
      final MutableFudgeMsg newMessage = serializer.newMessage();
      final FudgeFieldType doubleFieldType = FudgeWireType.DOUBLE;
      newMessage.add(null, NATIVE_FIELD_INDEX, doubleFieldType, value);
      return newMessage;
    } else if (value instanceof FudgeMsg) {
      final MutableFudgeMsg newMessage = serializer.newMessage();
      final FudgeFieldType messageFieldType = FudgeWireType.SUB_MESSAGE;
      newMessage.add(null, NATIVE_FIELD_INDEX, messageFieldType, value);
      return newMessage;
    }
    serializer.reset();
    final MutableFudgeMsg message = serializer.newMessage();
    try {
      serializer.addToMessageWithClassHeaders(message, null, NATIVE_FIELD_INDEX, WriteReplaceHelper.writeReplace(value));
    } catch (FudgeRuntimeException e) {
      s_logger.error("Can't encode value {}", value);
      s_logger.warn("Caught exception", e);
    }
    // Optimize the "value encoded as sub-message" case to reduce space requirement
    final Object svalue = message.getValue(NATIVE_FIELD_INDEX);
    if (svalue instanceof FudgeMsg) {
      return (FudgeMsg) svalue;
    } else {
      return message;
    }
  }

  protected static Object deserializeValue(final FudgeDeserializer deserializer, final FudgeMsg message) {
    deserializer.reset();
    if (message.getNumFields() == 1) {
      final Object value = message.getValue(NATIVE_FIELD_INDEX);
      if (value != null) {
        return value;
      }
    }
    return deserializer.fudgeMsgToObject(message);
  }

  @Override
  public Integer estimateValueSize(final ComputedValue value) {
    if (value.getValue() == null) {
      return null;
    }
    final Integer classSize = _valueSizeByClassCache.get(value.getValue().getClass());
    if (classSize != null) {
      return classSize;
    }
    return getValueSizeCache().get(value.getSpecification());
  }

  @Override
  public Iterator<Pair<ValueSpecification, FudgeMsg>> iterator() {
    // TODO 2008-08-09 Implement this; iterate over the values in the data store
    throw new UnsupportedOperationException();
  }

  /**
   * Remove any underlying resources from the data stores and make the size cache available for garbage collection.
   */
  public void delete() {
    _valueSizeCache.remove(); //TODO this is not right
    getPrivateDataStore().delete();
    if (getSharedDataStore() != getPrivateDataStore()) {
      getSharedDataStore().delete();
    }
  }

}
TOP

Related Classes of com.opengamma.engine.cache.DefaultViewComputationCache$MissingValueLoader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.