/** * Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.integration.viewer.status.impl; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.threeten.bp.Instant; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.opengamma.OpenGammaRuntimeException; import com.opengamma.core.config.impl.ConfigItem; import com.opengamma.core.position.Portfolio; import com.opengamma.core.position.Position; import com.opengamma.core.position.PositionSource; import com.opengamma.core.position.Trade; import com.opengamma.engine.ComputationTargetSpecification; import com.opengamma.engine.calcnode.MissingValue; import com.opengamma.engine.marketdata.spec.MarketDataSpecification; import com.opengamma.engine.target.ComputationTargetType; import com.opengamma.engine.value.ComputedValueResult; import com.opengamma.engine.value.ValueProperties; import com.opengamma.engine.value.ValueRequirement; import com.opengamma.engine.value.ValueSpecification; import com.opengamma.engine.view.ViewCalculationConfiguration; import com.opengamma.engine.view.ViewCalculationResultModel; import com.opengamma.engine.view.ViewComputationResultModel; import com.opengamma.engine.view.ViewDefinition; import com.opengamma.engine.view.ViewDeltaResultModel; import com.opengamma.engine.view.ViewProcessor; import com.opengamma.engine.view.client.ViewClient; import com.opengamma.engine.view.compilation.CompiledViewCalculationConfiguration; import com.opengamma.engine.view.compilation.CompiledViewDefinition; import com.opengamma.engine.view.cycle.ViewCycleMetadata; import com.opengamma.engine.view.execution.ExecutionOptions; import com.opengamma.engine.view.execution.ViewCycleExecutionOptions; import com.opengamma.engine.view.listener.AbstractViewResultListener; import com.opengamma.financial.aggregation.CurrenciesAggregationFunction; import com.opengamma.financial.aggregation.PortfolioAggregator; import com.opengamma.financial.tool.ToolContext; import com.opengamma.id.UniqueId; import com.opengamma.id.VersionCorrection; import com.opengamma.integration.viewer.status.ViewStatus; import com.opengamma.livedata.UserPrincipal; import com.opengamma.master.config.ConfigMaster; import com.opengamma.master.config.ConfigMasterUtils; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.GUIDGenerator; import com.opengamma.util.tuple.Pair; /** * View status calculation task */ public class ViewStatusCalculationTask implements Callable<PerViewStatusResult> { private final class ViewStatusResultListener extends AbstractViewResultListener { private final CountDownLatch _latch; private final PerViewStatusResult _statusResult; private final ViewDefinition _viewDefinition; private AtomicLong _count = new AtomicLong(0); private ViewStatusResultListener(CountDownLatch latch, PerViewStatusResult statusResult, ViewDefinition viewDefinition) { _latch = latch; _statusResult = statusResult; _viewDefinition = viewDefinition; } @Override public UserPrincipal getUser() { return _user; } @Override public void viewDefinitionCompiled(CompiledViewDefinition compiledViewDefinition, boolean hasMarketDataPermissions) { s_logger.error("View definition compiled"); CompiledViewCalculationConfiguration compiledCalculationConfiguration = compiledViewDefinition.getCompiledCalculationConfiguration(DEFAULT_CALC_CONFIG); Map<ValueSpecification, Set<ValueRequirement>> terminalOutputs = compiledCalculationConfiguration.getTerminalOutputSpecifications(); for (ValueSpecification valueSpec : terminalOutputs.keySet()) { ComputationTargetType computationTargetType = valueSpec.getTargetSpecification().getType(); if (isValidTargetType(computationTargetType)) { UniqueId uniqueId = valueSpec.getTargetSpecification().getUniqueId(); String currency = getCurrency(uniqueId, computationTargetType); if (currency != null) { _statusResult.put(new ViewStatusKeyBean(_securityType, valueSpec.getValueName(), currency, computationTargetType.getName()), ViewStatus.NO_VALUE); } else { s_logger.error("Discarding result as NULL return as Currency for id: {} targetType:{}", uniqueId, computationTargetType); } } } } @Override public void viewDefinitionCompilationFailed(final Instant valuationTime, final Exception exception) { s_logger.debug("View definition {} failed to initialize", _viewDefinition); try { processGraphFailResult(_statusResult); } finally { _latch.countDown(); } } public void cycleStarted(ViewCycleMetadata cycleInfo) { s_logger.debug("Cycle started"); } @Override public void cycleExecutionFailed(ViewCycleExecutionOptions executionOptions, Exception exception) { s_logger.debug("Cycle execution failed", exception); } @Override public void processCompleted() { s_logger.debug("Process completed"); } @Override public void processTerminated(boolean executionInterrupted) { s_logger.debug("Process terminated"); } @Override public void clientShutdown(Exception e) { s_logger.debug("Client shutdown"); } @Override public void cycleFragmentCompleted(ViewComputationResultModel fullResult, ViewDeltaResultModel deltaResult) { s_logger.debug("cycle fragment completed"); } @Override public void cycleCompleted(final ViewComputationResultModel fullResult, final ViewDeltaResultModel deltaResult) { s_logger.debug("cycle {} completed", _count.get()); if (_count.getAndIncrement() > 5) { processStatusResult(fullResult, _statusResult); _latch.countDown(); } } } private static final String MIXED_CURRENCY = "MIXED_CURRENCY"; private static final Logger s_logger = LoggerFactory.getLogger(ViewStatusCalculationTask.class); private static final String DEFAULT_CALC_CONFIG = "Default"; private final String _securityType; private final Set<String> _valueRequirementNames; private final UniqueId _portfolioId; private final UserPrincipal _user; private final ToolContext _toolContext; private final CurrenciesAggregationFunction _currenciesAggrFunction; private final Map<UniqueId, String> _targetCurrenciesCache = Maps.newConcurrentMap(); private final MarketDataSpecification _marketDataSpecification; public ViewStatusCalculationTask(ToolContext toolcontext, UniqueId portfolioId, UserPrincipal user, String securityType, Collection<String> valueRequirementNames, MarketDataSpecification marketDataSpecification) { ArgumentChecker.notNull(portfolioId, "portfolioId"); ArgumentChecker.notNull(securityType, "securityType"); ArgumentChecker.notNull(valueRequirementNames, "valueRequirementNames"); ArgumentChecker.notNull(user, "user"); ArgumentChecker.notNull(toolcontext, "toolcontext"); ArgumentChecker.notNull(marketDataSpecification, "marketDataSpecification"); _portfolioId = portfolioId; _user = user; _securityType = securityType; _valueRequirementNames = ImmutableSet.copyOf(valueRequirementNames); _toolContext = toolcontext; _currenciesAggrFunction = new CurrenciesAggregationFunction(_toolContext.getSecuritySource()); _marketDataSpecification = marketDataSpecification; } @Override public PerViewStatusResult call() throws Exception { s_logger.debug("Start calculating result for security:{} with values{}", _securityType, Sets.newTreeSet(_valueRequirementNames).toString()); final PerViewStatusResult statusResult = new PerViewStatusResult(_securityType); //No need to do any work if there are no ValueRequirements to compute if (_valueRequirementNames.isEmpty()) { return statusResult; } final ViewDefinition viewDefinition = createViewDefinition(); final ViewProcessor viewProcessor = _toolContext.getViewProcessor(); final ViewClient client = viewProcessor.createViewClient(_user); final CountDownLatch latch = new CountDownLatch(1); client.setResultListener(new ViewStatusResultListener(latch, statusResult, viewDefinition)); client.attachToViewProcess(viewDefinition.getUniqueId(), ExecutionOptions.infinite(_marketDataSpecification)); try { s_logger.info("main thread waiting"); if (!latch.await(30, TimeUnit.SECONDS)) { s_logger.error("Timed out waiting for {}", viewDefinition); } } catch (final InterruptedException ex) { throw new OpenGammaRuntimeException("Interrupted while waiting for " + viewDefinition, ex); } client.detachFromViewProcess(); removeViewDefinition(viewDefinition); s_logger.debug("PerViewStatusResult for securityType:{} is {}", _securityType, statusResult); return statusResult; } protected boolean isValidTargetType(final ComputationTargetType computationTargetType) { if (ComputationTargetType.POSITION.isCompatible(computationTargetType) || ComputationTargetType.PORTFOLIO.isCompatible(computationTargetType) || ComputationTargetType.PORTFOLIO_NODE.isCompatible(computationTargetType) || ComputationTargetType.TRADE.isCompatible(computationTargetType)) { return true; } return false; } private void removeViewDefinition(ViewDefinition viewDefinition) { s_logger.debug("Removing ViewDefintion with id: {}", viewDefinition.getUniqueId()); ConfigMaster configMaster = _toolContext.getConfigMaster(); configMaster.remove(viewDefinition.getUniqueId().getObjectId()); s_logger.debug("ViewDefinition {} removed", viewDefinition.getUniqueId()); } private ViewDefinition createViewDefinition() { final ViewDefinition viewDefinition = new ViewDefinition("VS_VIEW_" + GUIDGenerator.generate().toString(), _portfolioId, _user); viewDefinition.setMaxDeltaCalculationPeriod(500L); viewDefinition.setMaxFullCalculationPeriod(500L); viewDefinition.setMinDeltaCalculationPeriod(500L); viewDefinition.setMinFullCalculationPeriod(500L); ViewCalculationConfiguration defaultCalConfig = new ViewCalculationConfiguration(viewDefinition, DEFAULT_CALC_CONFIG); for (String requiredOutput : _valueRequirementNames) { defaultCalConfig.addPortfolioRequirementName(_securityType, requiredOutput); } viewDefinition.addViewCalculationConfiguration(defaultCalConfig); return storeViewDefinition(viewDefinition); } private ViewDefinition storeViewDefinition(final ViewDefinition viewDefinition) { ConfigItem<ViewDefinition> config = ConfigItem.of(viewDefinition, viewDefinition.getName(), ViewDefinition.class); config = ConfigMasterUtils.storeByName(_toolContext.getConfigMaster(), config); return config.getValue(); } private void processGraphFailResult(final PerViewStatusResult statusResult) { PositionSource positionSource = _toolContext.getPositionSource(); Portfolio portfolio = positionSource.getPortfolio(_portfolioId, VersionCorrection.LATEST); List<Position> positions = PortfolioAggregator.flatten(portfolio); Set<String> currencies = Sets.newHashSet(); for (Position position : positions) { if (position.getSecurity() == null) { position.getSecurityLink().resolve(_toolContext.getSecuritySource()); } if (position.getSecurity() != null && _securityType.equals(position.getSecurity().getSecurityType())) { currencies.add(getCurrency(position.getUniqueId(), ComputationTargetType.POSITION)); } } for (String valueName : _valueRequirementNames) { for (String currency : currencies) { statusResult.put(new ViewStatusKeyBean(_securityType, valueName, currency, ComputationTargetType.POSITION.getName()), ViewStatus.GRAPH_FAIL); } } } private void processStatusResult(ViewComputationResultModel fullResult, PerViewStatusResult statusResult) { ViewCalculationResultModel calculationResult = fullResult.getCalculationResult(DEFAULT_CALC_CONFIG); Collection<ComputationTargetSpecification> allTargets = calculationResult.getAllTargets(); for (ComputationTargetSpecification targetSpec : allTargets) { ComputationTargetType targetType = targetSpec.getSpecification().getType(); if (isValidTargetType(targetType)) { Map<Pair<String, ValueProperties>, ComputedValueResult> values = calculationResult.getValues(targetSpec); for (Map.Entry<Pair<String, ValueProperties>, ComputedValueResult> valueEntry : values.entrySet()) { String valueName = valueEntry.getKey().getFirst(); String currency = getCurrency(targetSpec.getUniqueId(), targetType); s_logger.debug("{} currency returned for id:{} targetType:{}", currency, targetSpec.getUniqueId(), targetType); if (currency != null) { ComputedValueResult computedValue = valueEntry.getValue(); if (isGoodValue(computedValue)) { statusResult.put(new ViewStatusKeyBean(_securityType, valueName, currency, targetType.getName()), ViewStatus.VALUE); } } else { s_logger.error("Discarding result as NULL return as Currency for id: {} targetType:{}", targetSpec.getUniqueId(), targetType); } } } } } private String getCurrency(UniqueId uniqueId, ComputationTargetType computationTargetType) { synchronized (_targetCurrenciesCache) { String currency = _targetCurrenciesCache.get(uniqueId); if (currency == null) { if (ComputationTargetType.PORTFOLIO_NODE.isCompatible(computationTargetType) || ComputationTargetType.PORTFOLIO.isCompatible(computationTargetType)) { currency = MIXED_CURRENCY; } else if (ComputationTargetType.POSITION.isCompatible(computationTargetType)) { PositionSource positionSource = _toolContext.getPositionSource(); Position position = positionSource.getPosition(uniqueId); if (position.getSecurity() == null) { position.getSecurityLink().resolve(_toolContext.getSecuritySource()); } if (position.getSecurity() != null) { currency = _currenciesAggrFunction.classifyPosition(position); } } else if (ComputationTargetType.TRADE.isCompatible(computationTargetType)) { PositionSource positionSource = _toolContext.getPositionSource(); Trade trade = positionSource.getTrade(uniqueId); if (trade.getSecurity() == null) { trade.getSecurityLink().resolve(_toolContext.getSecuritySource()); } if (trade.getSecurity() != null) { currency = CurrenciesAggregationFunction.classifyBasedOnSecurity(trade.getSecurity(), _toolContext.getSecuritySource()); } } } if (currency == null) { currency = CurrenciesAggregationFunction.NO_CURRENCY; } _targetCurrenciesCache.put(uniqueId, currency); return currency; } } private boolean isGoodValue(final ComputedValueResult computedValue) { if (computedValue == null || computedValue.getValue() == null || StringUtils.EMPTY.equals(computedValue.getValue())) { return false; } else { return !(computedValue.getValue() instanceof MissingValue); } } }