com.opengamma.financial.view.ViewEvaluationFunction.java Source code

Java tutorial

Introduction

Here is the source code for com.opengamma.financial.view.ViewEvaluationFunction.java

Source

/**
 * 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);

}