/** * Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.web.server; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.Executors; import java.util.concurrent.locks.ReentrantLock; import org.apache.commons.lang.StringUtils; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.opengamma.OpenGammaRuntimeException; import com.opengamma.core.config.ConfigSource; import com.opengamma.core.config.impl.ConfigItem; import com.opengamma.core.position.Portfolio; import com.opengamma.core.position.PositionSource; import com.opengamma.core.security.SecuritySource; import com.opengamma.engine.view.ViewDefinition; import com.opengamma.engine.view.compilation.PortfolioCompiler; import com.opengamma.financial.aggregation.AggregationFunction; import com.opengamma.financial.aggregation.PortfolioAggregator; import com.opengamma.financial.portfolio.save.SavePortfolio; import com.opengamma.id.UniqueId; import com.opengamma.id.VersionCorrection; import com.opengamma.master.config.ConfigDocument; import com.opengamma.master.config.ConfigMaster; import com.opengamma.master.portfolio.PortfolioMaster; import com.opengamma.master.position.PositionMaster; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.tuple.Pair; import com.opengamma.util.tuple.Pairs; /** * Manages the lifecycle of aggregated view definitions. There is really no such thing as an aggregated view * definition, only an aggregated portfolio, but the web client exposes the idea of aggregating a view definition as a * shortcut for aggregating the underlying portfolio and requesting the same outputs. */ public class AggregatedViewDefinitionManager { private final PositionSource _positionSource; private final SecuritySource _securitySource; private final ConfigSource _combinedConfigSource; private final ConfigMaster _userConfigMaster; private final PortfolioMaster _userPortfolioMaster; private final Map<String, AggregationFunction<?>> _portfolioAggregators; private final SavePortfolio _portfolioSaver; private final ReentrantLock _lock = new ReentrantLock(); private final Map<Pair<UniqueId, List<String>>, PortfolioReference> _aggregatedPortfolios = Maps.newHashMap(); private final Map<Pair<UniqueId, List<String>>, ViewDefinitionReference> _aggregatedViewDefinitions = Maps.newHashMap(); public AggregatedViewDefinitionManager(PositionSource positionSource, SecuritySource securitySource, ConfigSource combinedConfigSource, ConfigMaster userConfigMaster, PortfolioMaster userPortfolioMaster, PositionMaster userPositionMaster, Map<String, AggregationFunction<?>> portfolioAggregators) { _positionSource = positionSource; _securitySource = securitySource; _combinedConfigSource = combinedConfigSource; _userConfigMaster = userConfigMaster; _userPortfolioMaster = userPortfolioMaster; _portfolioAggregators = portfolioAggregators; _portfolioSaver = new SavePortfolio(Executors.newSingleThreadExecutor(), userPortfolioMaster, userPositionMaster); } public Set<String> getAggregatorNames() { return ImmutableSet.copyOf(_portfolioAggregators.keySet()); } public UniqueId getViewDefinitionId(UniqueId baseViewDefinitionId, String aggregatorName) { List<String> aggregators; if (aggregatorName != null) { aggregators = Collections.singletonList(aggregatorName); } else { aggregators = Collections.emptyList(); } return getViewDefinitionId(baseViewDefinitionId, aggregators); } public UniqueId getViewDefinitionId(UniqueId baseViewDefinitionId, List<String> aggregatorNames) { // TODO: what about changes to the base view definition? ArgumentChecker.notNull(baseViewDefinitionId, "baseViewDefinitionId"); ArgumentChecker.notNull(aggregatorNames, "aggregatorNames"); ViewDefinition baseViewDefinition = (ViewDefinition) _combinedConfigSource.get(baseViewDefinitionId).getValue(); if (baseViewDefinition == null) { throw new OpenGammaRuntimeException("Unknown view definition with unique ID " + baseViewDefinitionId); } UniqueId basePortfolioId = baseViewDefinition.getPortfolioId(); if (aggregatorNames.isEmpty() || basePortfolioId == null) { return baseViewDefinitionId; } Pair<UniqueId, List<String>> aggregatedViewDefinitionKey = Pairs.of(baseViewDefinition.getUniqueId(), aggregatorNames); Pair<UniqueId, List<String>> aggregatedPortfolioKey = Pairs.of(basePortfolioId, aggregatorNames); _lock.lock(); try { ViewDefinitionReference aggregatedViewDefinitionReference = _aggregatedViewDefinitions.get(aggregatedViewDefinitionKey); if (aggregatedViewDefinitionReference == null) { PortfolioReference aggregatedPortfolioReference = _aggregatedPortfolios.get(aggregatedPortfolioKey); if (aggregatedPortfolioReference == null) { UniqueId aggregatedPortfolioId = aggregatePortfolio(basePortfolioId, aggregatorNames); aggregatedPortfolioReference = new PortfolioReference(basePortfolioId, aggregatedPortfolioId); _aggregatedPortfolios.put(aggregatedPortfolioKey, aggregatedPortfolioReference); } String aggregatedViewDefinitionName = getAggregatedViewDefinitionName(baseViewDefinition.getName(), aggregatorNames); ViewDefinition aggregatedViewDefinition = baseViewDefinition.copyWith(aggregatedViewDefinitionName, aggregatedPortfolioReference.incrementReferenceCount(), baseViewDefinition.getMarketDataUser()); // Treat as a transient view definition that should not be persistent //aggregatedViewDefinition.setPersistent(false); ConfigItem<ViewDefinition> configItem = ConfigItem.of(aggregatedViewDefinition); configItem.setName(aggregatedViewDefinition.getName()); UniqueId viewDefinitionId = _userConfigMaster.add(new ConfigDocument(configItem)).getUniqueId(); aggregatedViewDefinitionReference = new ViewDefinitionReference(viewDefinitionId, aggregatedPortfolioReference); _aggregatedViewDefinitions.put(aggregatedViewDefinitionKey, aggregatedViewDefinitionReference); } return aggregatedViewDefinitionReference.incrementReferenceCount(); } finally { _lock.unlock(); } } public void releaseViewDefinition(UniqueId baseViewDefinitionId, String aggregatorName) { List<String> aggregatorNames; if (aggregatorName != null) { aggregatorNames = Collections.singletonList(aggregatorName); } else { aggregatorNames = Collections.emptyList(); } releaseViewDefinition(baseViewDefinitionId, aggregatorNames); } public void releaseViewDefinition(UniqueId baseViewDefinitionId, List<String> aggregatorNames) { Pair<UniqueId, List<String>> aggregatedViewDefinitionKey = Pairs.of(baseViewDefinitionId, aggregatorNames); ViewDefinitionReference viewDefinitionReference = _aggregatedViewDefinitions.get(aggregatedViewDefinitionKey); if (viewDefinitionReference == null) { return; } _lock.lock(); try { if (viewDefinitionReference.decrementReferenceCount() <= 0) { PortfolioReference portfolioReference = viewDefinitionReference.getPortfolioReference(); if (portfolioReference.decrementReferenceCount() <= 0) { _userPortfolioMaster.remove(portfolioReference.getPortfolioId()); Pair<UniqueId, List<String>> aggregatedPortfolioKey = Pairs.of(portfolioReference.getBasePortfolioId(), aggregatorNames); _aggregatedPortfolios.remove(aggregatedPortfolioKey); } _userConfigMaster.remove(viewDefinitionReference.getViewDefinitionId()); _aggregatedViewDefinitions.remove(aggregatedViewDefinitionKey); } } finally { _lock.unlock(); } } private String getAggregatedViewDefinitionName(String baseViewDefinitionName, List<String> aggregatorNames) { return baseViewDefinitionName + " aggregated by " + StringUtils.join(aggregatorNames, ", "); } private UniqueId aggregatePortfolio(UniqueId basePortfolioId, List<String> aggregatorNames) { // REVIEW jonathan 2011-11-13 -- portfolio aggregation is currently painful. The positions obtained from the // position source during the orginal portfolio lookup contain munged identifiers that do not correspond to // anything in the position master. We end up rewriting the positions even though there is no need, then we cannot // clean them up when the portfolio is no longer required in case other portfolios have now referenced the new // positions. Portfolio basePortfolio = _positionSource.getPortfolio(basePortfolioId, VersionCorrection.LATEST); Portfolio resolvedPortfolio = PortfolioCompiler.resolvePortfolio(basePortfolio, Executors.newSingleThreadExecutor(), _securitySource); List<AggregationFunction<?>> aggregationFunctions = Lists.newArrayListWithCapacity(aggregatorNames.size()); for (String aggregatorName : aggregatorNames) { AggregationFunction<?> aggregationFunction = _portfolioAggregators.get(aggregatorName); if (aggregationFunction == null) { throw new OpenGammaRuntimeException("Unknown aggregator '" + aggregatorName + "'"); } aggregationFunctions.add(aggregationFunction); } PortfolioAggregator aggregator = new PortfolioAggregator(aggregationFunctions); Portfolio aggregatedPortfolio = aggregator.aggregate(resolvedPortfolio); return _portfolioSaver.savePortfolio(aggregatedPortfolio, false); } //------------------------------------------------------------------------- private static class PortfolioReference { private final UniqueId _basePortfolioId; private final UniqueId _portfolioId; private long _referenceCount; public PortfolioReference(UniqueId basePortfolioId, UniqueId portfolioId) { _basePortfolioId = basePortfolioId; _portfolioId = portfolioId; } public UniqueId getBasePortfolioId() { return _basePortfolioId; } public UniqueId getPortfolioId() { return _portfolioId; } public UniqueId incrementReferenceCount() { _referenceCount++; return _portfolioId; } public long decrementReferenceCount() { return --_referenceCount; } } private static class ViewDefinitionReference { private final UniqueId _viewDefinitionId; private final PortfolioReference _portfolioReference; private long _referenceCount; public ViewDefinitionReference(UniqueId viewDefinitionId, PortfolioReference portfolioReference) { _viewDefinitionId = viewDefinitionId; _portfolioReference = portfolioReference; } public UniqueId getViewDefinitionId() { return _viewDefinitionId; } public PortfolioReference getPortfolioReference() { return _portfolioReference; } public UniqueId incrementReferenceCount() { _referenceCount++; return _viewDefinitionId; } public long decrementReferenceCount() { return --_referenceCount; } } }