/** * Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.engine.view.impl; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReentrantLock; import com.opengamma.engine.depgraph.DependencyGraphExplorer; import com.opengamma.engine.depgraph.DependencyNode; import com.opengamma.engine.value.ValueSpecification; import com.opengamma.engine.view.ExecutionLogMode; import com.opengamma.engine.view.compilation.CompiledViewDefinitionWithGraphs; import com.opengamma.util.tuple.Pair; /** * Represents a queryable source of the execution log mode to apply for a value specification. */ public class ExecutionLogModeSource { private final ReentrantLock _lock = new ReentrantLock(); private final Map<Pair<String, ValueSpecification>, Integer> _elevatedLogTargets = new HashMap<Pair<String, ValueSpecification>, Integer>(); private final Map<String, Map<ValueSpecification, Integer>> _elevatedLogNodes = new ConcurrentHashMap<String, Map<ValueSpecification, Integer>>(); private final Map<ValueSpecification, Integer> _elevatedLogSpecs = new ConcurrentHashMap<ValueSpecification, Integer>(); private CompiledViewDefinitionWithGraphs _compiledViewDefinition; /** * Ensures at least a minimum level of logging output is present in the results for the given value specifications. Changes will take effect from the next computation cycle. * <p> * Each call to elevate the minimum level of logging output for a result must be paired with exactly one call to reduce the level of logging output, if required. * * @param minimumLogMode the minimum log mode to ensure, not null * @param targets the targets affected, not null or empty */ public void setMinimumLogMode(ExecutionLogMode minimumLogMode, Set<Pair<String, ValueSpecification>> targets) { // Synchronization ensures only one writer, while getLogMode is allowed to read from the ConcurrentHashMap // without further locking. switch (minimumLogMode) { case INDICATORS: _lock.lock(); try { for (Pair<String, ValueSpecification> target : targets) { if (decrementRefCount(target, _elevatedLogTargets)) { removeElevatedNode(target); } } } finally { _lock.unlock(); } break; case FULL: _lock.lock(); try { for (Pair<String, ValueSpecification> target : targets) { if (incrementRefCount(target, _elevatedLogTargets)) { addElevatedNode(target); } } } finally { _lock.unlock(); } break; } } /** * Gets the log mode for a dependency node. * * @param calcConfig the calculation configuration, not null * @param output an output from the node, not null * @return the log mode, not null */ public ExecutionLogMode getLogMode(final String calcConfig, final ValueSpecification output) { Map<ValueSpecification, Integer> modesByConfig = _elevatedLogNodes.get(calcConfig); if (modesByConfig == null) { return ExecutionLogMode.INDICATORS; } return modesByConfig.containsKey(output) ? ExecutionLogMode.FULL : ExecutionLogMode.INDICATORS; } //------------------------------------------------------------------------- /*package*/void viewDefinitionCompiled(CompiledViewDefinitionWithGraphs compiledViewDefinition) { _lock.lock(); try { _compiledViewDefinition = compiledViewDefinition; rebuildNodeLogModes(); } finally { _lock.unlock(); } } private void addElevatedNode(Pair<String, ValueSpecification> target) { // Must be called while holding the lock incrementNodeRefCount(getNodeProducing(target), getOrCreateModes(target.getFirst())); } private void incrementNodeRefCount(DependencyNode node, Map<ValueSpecification, Integer> refCounts) { if (node == null) { return; } int count = node.getOutputCount(); for (int i = 0; i < count; i++) { incrementRefCount(node.getOutputValue(i), refCounts); } count = node.getInputCount(); for (int i = 0; i < count; i++) { incrementNodeRefCount(node.getInputNode(i), refCounts); } } private void removeElevatedNode(Pair<String, ValueSpecification> target) { // Must be called while holding the lock decrementNodeRefCount(getNodeProducing(target), getOrCreateModes(target.getFirst())); } private void decrementNodeRefCount(DependencyNode node, Map<ValueSpecification, Integer> refCounts) { if (node == null) { return; } int count = node.getOutputCount(); for (int i = 0; i < count; i++) { decrementRefCount(node.getOutputValue(i), refCounts); } count = node.getInputCount(); for (int i = 0; i < count; i++) { decrementNodeRefCount(node.getInputNode(i), refCounts); } } private DependencyNode getNodeProducing(Pair<String, ValueSpecification> target) { if (_compiledViewDefinition == null) { return null; } String calcConfigName = target.getFirst(); ValueSpecification valueSpec = target.getSecond(); final DependencyGraphExplorer explorer; try { explorer = _compiledViewDefinition.getDependencyGraphExplorer(calcConfigName); } catch (Exception e) { // No graph available return null; } return explorer.getNodeProducing(valueSpec); } private void rebuildNodeLogModes() { // Must be called while holding the lock _elevatedLogNodes.clear(); for (Pair<String, ValueSpecification> target : _elevatedLogTargets.keySet()) { addElevatedNode(target); } } private Map<ValueSpecification, Integer> getOrCreateModes(final String calcConfig) { Map<ValueSpecification, Integer> modesByConfig = _elevatedLogNodes.get(calcConfig); if (modesByConfig == null) { modesByConfig = new ConcurrentHashMap<ValueSpecification, Integer>(); _elevatedLogNodes.put(calcConfig, modesByConfig); } return modesByConfig; } private static <T> boolean incrementRefCount(T key, Map<T, Integer> refMap) { Integer refCount = refMap.get(key); if (refCount == null) { refMap.put(key, 1); return true; } else { refMap.put(key, refCount + 1); return false; } } private static <T> boolean decrementRefCount(T key, Map<T, Integer> refMap) { Integer value = refMap.get(key); if (value == null) { return false; } if (value == 1) { refMap.remove(key); return true; } else { refMap.put(key, value - 1); return false; } } }