Package com.opengamma.financial.view

Source Code of com.opengamma.financial.view.ViewEvaluationFunction

/**
* Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.financial.view;

import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.threeten.bp.Instant;

import com.google.common.collect.Sets;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.core.config.impl.ConfigItem;
import com.opengamma.engine.ComputationTarget;
import com.opengamma.engine.ComputationTargetSpecification;
import com.opengamma.engine.function.AbstractFunction;
import com.opengamma.engine.function.FunctionCompilationContext;
import com.opengamma.engine.function.FunctionExecutionContext;
import com.opengamma.engine.function.FunctionInputs;
import com.opengamma.engine.target.ComputationTargetType;
import com.opengamma.engine.value.ComputedValue;
import com.opengamma.engine.value.ValueProperties;
import com.opengamma.engine.value.ValueRequirement;
import com.opengamma.engine.value.ValueSpecification;
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.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.execution.ViewExecutionFlags;
import com.opengamma.engine.view.listener.ViewResultListener;
import com.opengamma.financial.OpenGammaExecutionContext;
import com.opengamma.id.UniqueId;
import com.opengamma.livedata.UserPrincipal;
import com.opengamma.master.config.ConfigDocument;
import com.opengamma.master.config.ConfigMaster;
import com.opengamma.master.config.ConfigSearchRequest;
import com.opengamma.master.config.ConfigSearchResult;
import com.opengamma.util.async.AsynchronousExecution;
import com.opengamma.util.async.AsynchronousOperation;
import com.opengamma.util.async.ResultCallback;

/**
* Runs an arbitrary execution sequence on a view definition to produce values for one or more targets. The job is encoded into a {@link ViewEvaluationTarget}.
*
* @param <TTarget> the computation target type
* @param <TResultBuilder> the type of the result builder used throughout execution
*/
public abstract class ViewEvaluationFunction<TTarget extends ViewEvaluationTarget, TResultBuilder> extends AbstractFunction.NonCompiledInvoker {

  /**
   * Name of the property used to distinguish the outputs when the view definition has multiple calculation configurations.
   */
  public static final String PROPERTY_CALC_CONFIG = "config";

  private static final Logger s_logger = LoggerFactory.getLogger(ViewEvaluationFunction.class);

  private final String _valueRequirementName;
  private final ComputationTargetType _targetType;

  public ViewEvaluationFunction(String valueRequirementName, Class<TTarget> targetType) {
    _valueRequirementName = valueRequirementName;
    _targetType = ComputationTargetType.of(targetType);
  }

  // CompiledFunctionDefinition

  @Override
  public ComputationTargetType getTargetType() {
    return _targetType;
  }

  @Override
  @SuppressWarnings("unchecked")
  public Set<ValueSpecification> getResults(final FunctionCompilationContext context, final ComputationTarget target) {
    final TTarget viewEvaluation = (TTarget) target.getValue();
    final Collection<String> calcConfigs = viewEvaluation.getViewDefinition().getAllCalculationConfigurationNames();
    final Set<ValueSpecification> results = Sets.newHashSetWithExpectedSize(calcConfigs.size());
    final ComputationTargetSpecification targetSpec = target.toSpecification();
    for (final String calcConfig : calcConfigs) {
      results.add(getResultSpec(calcConfig, targetSpec));
    }
    return results;
  }

  @Override
  public Set<ValueRequirement> getRequirements(final FunctionCompilationContext context, final ComputationTarget target, final ValueRequirement desiredValue) {
    return Collections.emptySet();
  }

  protected UniqueId storeViewDefinition(final FunctionExecutionContext executionContext, final UniqueId targetId, final ViewDefinition viewDefinition) {
    final String name = targetId.toString(); // Use the tempTarget identifier as a name to reuse an existing config item
    final ConfigMaster master = OpenGammaExecutionContext.getConfigMaster(executionContext);
    if (master == null) {
      throw new IllegalStateException("Execution context does not contain a " + OpenGammaExecutionContext.CONFIG_MASTER_NAME);
    }
    final ConfigSearchRequest<ViewDefinition> request = new ConfigSearchRequest<ViewDefinition>(ViewDefinition.class);
    request.setName(name);
    final ConfigSearchResult<ViewDefinition> result = master.search(request);
    if (result.getDocuments() != null) {
      for (final ConfigDocument document : result.getDocuments()) {
        if (viewDefinition.equals(document.getValue().getValue())) {
          // Found a matching one
          s_logger.debug("Using previous view definition {}", document.getUniqueId());
          return document.getUniqueId();
        } else {
          // Found a dead one; either our temp target unique identifiers are not unique (different repositories MUST have different schemes) or the identifier
          // sequence has been restarted/repeated and is colliding with old or dead configuration documents.
          s_logger.info("Deleting expired view definition {}", document.getUniqueId());
          master.removeVersion(document.getUniqueId());
        }
      }
    }
    final ConfigItem<ViewDefinition> item = ConfigItem.of(viewDefinition);
    item.setName(name);
    final UniqueId uid = master.add(new ConfigDocument(item)).getUniqueId();
    s_logger.info("Created new view definition {} for {}", uid, name);
    return uid;
  }

  // FunctionInvoker

  @Override
  public Set<ComputedValue> execute(final FunctionExecutionContext executionContext, final FunctionInputs inputs, final ComputationTarget target,
      final Set<ValueRequirement> desiredValues) throws AsynchronousExecution {
    final ViewEvaluationTarget viewEvaluation = (ViewEvaluationTarget) target.getValue();
    final UniqueId viewId = storeViewDefinition(executionContext, target.getUniqueId(), viewEvaluation.getViewDefinition());
    final ViewProcessor viewProcessor = OpenGammaExecutionContext.getViewProcessor(executionContext);
    if (viewProcessor == null) {
      throw new IllegalStateException("Execution context does not contain a " + OpenGammaExecutionContext.VIEW_PROCESSOR_NAME);
    }
    final ViewClient viewClient = viewProcessor.createViewClient(viewEvaluation.getViewDefinition().getMarketDataUser());
    final UniqueId viewClientId = viewClient.getUniqueId();
    s_logger.info("Created view client {}, connecting to {}", viewClientId, viewId);
    viewClient.attachToViewProcess(viewId,
        ExecutionOptions.of(viewEvaluation.getExecutionSequence().createSequence(executionContext), getDefaultCycleOptions(executionContext), getViewExecutionFlags(desiredValues)), true);
    final TResultBuilder resultBuilder = createResultBuilder(viewEvaluation, desiredValues);
    final AsynchronousOperation<Set<ComputedValue>> async = AsynchronousOperation.createSet();
    final AtomicReference<ResultCallback<Set<ComputedValue>>> asyncResult = new AtomicReference<ResultCallback<Set<ComputedValue>>>(async.getCallback());
    viewClient.setResultListener(new ViewResultListener() {

      private void reportException(final RuntimeException e) {
        final ResultCallback<?> callback = asyncResult.getAndSet(null);
        if (callback != null) {
          try {
            callback.setException(e);
          } finally {
            s_logger.info("Shutting down view client {}", viewClientId);
            viewClient.shutdown();
          }
        } else {
          s_logger.warn("Callback already made before exception for {}", viewClientId);
        }
      }

      private void reportResult(final Set<ComputedValue> values) {
        final ResultCallback<Set<ComputedValue>> callback = asyncResult.getAndSet(null);
        if (callback != null) {
          try {
            callback.setResult(values);
          } finally {
            s_logger.info("Shutting down view client {}", viewClientId);
            viewClient.shutdown();
          }
        } else {
          s_logger.warn("Callback already made before results for {}", viewClientId);
        }
      }

      @Override
      public UserPrincipal getUser() {
        return viewEvaluation.getViewDefinition().getMarketDataUser();
      }

      @Override
      public void viewDefinitionCompiled(final CompiledViewDefinition compiledViewDefinition, final boolean hasMarketDataPermissions) {
        s_logger.debug("View definition compiled for {}", viewClientId);
        try {
          store(compiledViewDefinition, resultBuilder);
        } catch (final RuntimeException e) {
          s_logger.error("Caught exception during compilation completed callback", e);
          reportException(e);
        }
      }

      @Override
      public void viewDefinitionCompilationFailed(final Instant valuationTime, final Exception exception) {
        s_logger.error("View compilation failure for {} - {}", viewClientId, exception);
        reportException(new OpenGammaRuntimeException("View definition compilation failed for " + valuationTime, exception));
      }

      @Override
      public void cycleStarted(final ViewCycleMetadata cycleMetadata) {
        // This is good. Don't need to do anything.
        s_logger.debug("Cycle started for {}", viewClientId);
      }

      @Override
      public void cycleFragmentCompleted(final ViewComputationResultModel fullFragment, final ViewDeltaResultModel deltaFragment) {
        // This shouldn't happen. We've asked for full results only
        s_logger.error("Cycle fragment completed for {}", viewClientId);
        reportException(new OpenGammaRuntimeException("Assertion error"));
        assert false;
      }

      @Override
      public void cycleCompleted(final ViewComputationResultModel fullResult, final ViewDeltaResultModel deltaResult) {
        s_logger.debug("Cycle completed for {}", viewClientId);
        try {
          store(fullResult, resultBuilder);
        } catch (final RuntimeException e) {
          s_logger.error("Caught exception during cycle completed callback", e);
          reportException(e);
        }
      }

      @Override
      public void cycleExecutionFailed(final ViewCycleExecutionOptions executionOptions, final Exception exception) {
        s_logger.error("Cycle execution failed for {}", viewClientId);
        reportException(new OpenGammaRuntimeException("View cycle execution failed for " + executionOptions, exception));
      }

      @Override
      public void processCompleted() {
        s_logger.info("View process completed for {}", viewClientId);
        try {
          Set<ComputedValue> results = buildResults(target, resultBuilder);
          reportResult(results);
        } catch (final RuntimeException e) {
          s_logger.error("Caught exception during process completed callback", e);
          reportException(e);
        }
      }

      @Override
      public void processTerminated(final boolean executionInterrupted) {
        // Normally we would have expected one of the other notifications, so if the callback exists we report an error
        final ResultCallback<?> callback = asyncResult.getAndSet(null);
        if (callback != null) {
          s_logger.error("View process terminated for {}", viewClientId);
          reportException(new OpenGammaRuntimeException(executionInterrupted ? "Execution interrupted" : "View process terminated"));
        } else {
          s_logger.debug("View process terminated for {}", viewClientId);
        }
      }

      @Override
      public void clientShutdown(final Exception e) {
        // Normally we would have expected one of the other notifications or this in response to us calling "shutdown", so if the callback exists we report an error
        final ResultCallback<?> callback = asyncResult.getAndSet(null);
        if (callback != null) {
          s_logger.error("View client shutdown for {}", viewClientId);
          reportException(new OpenGammaRuntimeException("View client shutdown", e));
        } else {
          s_logger.debug("View client shutdown for {}", viewClientId);
        }
      }

    });
    viewClient.triggerCycle();
    return async.getResult();
  }

  //-------------------------------------------------------------------------
  protected ValueSpecification getResultSpec(String calcConfigName, ComputationTargetSpecification targetSpec) {
    ValueProperties.Builder properties = createValueProperties().withoutAny(PROPERTY_CALC_CONFIG).with(PROPERTY_CALC_CONFIG, calcConfigName);
    return new ValueSpecification(_valueRequirementName, targetSpec, properties.get());
  }

  protected EnumSet<ViewExecutionFlags> getViewExecutionFlags(Set<ValueRequirement> desiredValues) {
    return EnumSet.of(ViewExecutionFlags.WAIT_FOR_INITIAL_TRIGGER, ViewExecutionFlags.RUN_AS_FAST_AS_POSSIBLE, ViewExecutionFlags.SKIP_CYCLE_ON_NO_MARKET_DATA);
  }

  protected abstract ViewCycleExecutionOptions getDefaultCycleOptions(FunctionExecutionContext context);

  protected abstract TResultBuilder createResultBuilder(ViewEvaluationTarget viewEvaluation, Set<ValueRequirement> desiredValues);

  protected abstract void store(ViewComputationResultModel results, TResultBuilder resultBuilder);

  protected abstract void store(CompiledViewDefinition compiledViewDefinition, TResultBuilder resultBuilder);

  protected abstract Set<ComputedValue> buildResults(ComputationTarget target, TResultBuilder resultBuilder);

}
TOP

Related Classes of com.opengamma.financial.view.ViewEvaluationFunction

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.