/** * Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.engine.view.worker; import java.util.Collections; import java.util.List; import java.util.Set; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.opengamma.engine.marketdata.MarketDataPermissionProvider; import com.opengamma.engine.marketdata.MarketDataProvider; import com.opengamma.engine.marketdata.resolver.MarketDataProviderResolver; import com.opengamma.engine.marketdata.spec.MarketDataSpecification; import com.opengamma.engine.value.ValueProperties; import com.opengamma.engine.value.ValuePropertyNames; import com.opengamma.engine.value.ValueSpecification; import com.opengamma.livedata.UserPrincipal; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.tuple.Pair; import com.opengamma.util.tuple.Pairs; /** * A source of market data that aggregates data from multiple underlying {@link MarketDataProvider}s. */ public class ViewExecutionDataProvider { /** The market data user */ private final UserPrincipal _user; /** The underlying providers in priority order. */ private final List<MarketDataProvider> _providers; /** The market data specifications in priority order. */ private final List<MarketDataSpecification> _specs; private final MarketDataPermissionProvider _permissionsProvider; /** * @param user The user requesting the data, not null * @param specs Specifications of the underlying providers in priority order, not empty * @param resolver For resolving market data specifications into providers, not null * @throws IllegalArgumentException If any of the data providers in {@code specs} can't be resolved */ public ViewExecutionDataProvider(final UserPrincipal user, final List<MarketDataSpecification> specs, final MarketDataProviderResolver resolver) { ArgumentChecker.notNull(user, "user"); ArgumentChecker.notEmpty(specs, "specs"); ArgumentChecker.notNull(resolver, "resolver"); _user = user; _specs = ImmutableList.copyOf(specs); if (_specs.size() == 1) { final MarketDataProvider provider = resolver.resolve(user, _specs.get(0)); if (provider == null) { throw new IllegalArgumentException("Unable to resolve market data spec " + _specs.get(0)); } _providers = Collections.singletonList(provider); _permissionsProvider = provider.getPermissionProvider(); } else { _providers = Lists.newArrayListWithCapacity(_specs.size()); for (final MarketDataSpecification spec : _specs) { final MarketDataProvider provider = resolver.resolve(user, spec); if (provider == null) { throw new IllegalArgumentException("Unable to resolve market data spec " + spec); } _providers.add(provider); } _permissionsProvider = new CompositePermissionProvider(); } } public UserPrincipal getMarketDataUser() { return _user; } protected List<MarketDataProvider> getProviders() { return _providers; } public List<MarketDataSpecification> getSpecifications() { return _specs; } /** * @return An permissions provider backed by the permissions providers of the underlying market data providers */ public MarketDataPermissionProvider getPermissionProvider() { return _permissionsProvider; } /** * Divides up the specifications into a set for each underlying provider. The values from each underlying will have been tagged with a provider property which indicates the underlying they came * from. This is removed from the returned result so the original value specifications as used by the underlying are returned. * * @param numProviders the number of providers in total * @param specifications The market data specifications * @return A set of specifications for each underlying provider, in the same order as the providers */ protected static List<Set<ValueSpecification>> partitionSpecificationsByProvider(final int numProviders, final Set<ValueSpecification> specifications) { if (numProviders == 1) { return Collections.singletonList(specifications); } else { final List<Set<ValueSpecification>> result = Lists.newArrayListWithCapacity(numProviders); for (int i = 0; i < numProviders; i++) { result.add(Sets.<ValueSpecification>newHashSet()); } for (final ValueSpecification specification : specifications) { String provider = specification.getProperty(ValuePropertyNames.DATA_PROVIDER); if (provider != null) { final ValueProperties.Builder underlyingProperties = specification.getProperties().copy().withoutAny(ValuePropertyNames.DATA_PROVIDER); final int slash = provider.indexOf('/'); if (slash > 0) { underlyingProperties.with(ValuePropertyNames.DATA_PROVIDER, provider.substring(0, slash)); provider = provider.substring(slash + 1); } try { result.get(Integer.parseInt(provider)).add(new ValueSpecification(specification.getValueName(), specification.getTargetSpecification(), underlyingProperties.get())); } catch (final NumberFormatException e) { // Ignore } } } return result; } } /** * Identifies the provider a given specification is used for. The values from the underlying will have been tagged with a provider property which indicates the underlying. Thsi is removed from the * result so the original value specification as used by the underlying is returned. * * @param specification the specification to test * @return the provider index and the underlying's specification, or null if it could not be found */ protected static Pair<Integer, ValueSpecification> getProviderSpecification(final ValueSpecification specification) { String provider = specification.getProperty(ValuePropertyNames.DATA_PROVIDER); if (provider != null) { final ValueProperties.Builder underlyingProperties = specification.getProperties().copy().withoutAny(ValuePropertyNames.DATA_PROVIDER); final int slash = provider.indexOf('/'); if (slash > 0) { underlyingProperties.with(ValuePropertyNames.DATA_PROVIDER, provider.substring(0, slash)); provider = provider.substring(slash + 1); } try { return Pairs.of(Integer.parseInt(provider), new ValueSpecification(specification.getValueName(), specification.getTargetSpecification(), underlyingProperties.get())); } catch (final NumberFormatException e) { // Ignore } } return null; } /** * Converts a value specification as used by a given underlying to one that can be used by this provider. An integer identifier for the underlying provider will be put into a property that the * {@link #partitionSpecificationsByProvider} helper will use to map the specification back to the originating underlying. * * @param providerId the index of the provider in the list * @param underlying the value specification as used by the underlying * @return a value specification for external use */ protected static ValueSpecification convertUnderlyingSpecification(final int providerId, final ValueSpecification underlying) { final ValueProperties.Builder properties = underlying.getProperties().copy(); final String dataProvider = underlying.getProperty(ValuePropertyNames.DATA_PROVIDER); if (dataProvider != null) { properties.withoutAny(ValuePropertyNames.DATA_PROVIDER).with(ValuePropertyNames.DATA_PROVIDER, dataProvider + "/" + Integer.toString(providerId)); } else { properties.with(ValuePropertyNames.DATA_PROVIDER, Integer.toString(providerId)); } return new ValueSpecification(underlying.getValueName(), underlying.getTargetSpecification(), properties.get()); } /** * {@link MarketDataPermissionProvider} that checks the permissions using the underlying {@link MarketDataProvider}s. The underlying provider will be the one that returned the original availability * of the data. */ private class CompositePermissionProvider implements MarketDataPermissionProvider { /** * Checks permissions with the underlying providers and returns any requirements for which the user has no permissions with any provider. * * @param user The user whose market data permissions should be checked * @param specifications The market data to check access to * @return Values for which the user has no permissions with any of the underlying providers */ @Override public Set<ValueSpecification> checkMarketDataPermissions(final UserPrincipal user, final Set<ValueSpecification> specifications) { final List<Set<ValueSpecification>> specsByProvider = partitionSpecificationsByProvider(_providers.size(), specifications); final Set<ValueSpecification> missingSpecifications = Sets.newHashSet(); for (int i = 0; i < _providers.size(); i++) { final MarketDataPermissionProvider permissionProvider = _providers.get(i).getPermissionProvider(); final Set<ValueSpecification> specsForProvider = specsByProvider.get(i); final Set<ValueSpecification> missing = permissionProvider.checkMarketDataPermissions(user, specsForProvider); if (!missing.isEmpty()) { for (final ValueSpecification specification : missing) { missingSpecifications.add(convertUnderlyingSpecification(i, specification)); } } } return missingSpecifications; } } }