/**
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.integration.tool.depgraphambiguity;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.fudgemsg.FudgeMsg;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.component.ComponentRepository;
import com.opengamma.component.factory.engine.RemoteEngineContextsComponentFactory;
import com.opengamma.component.tool.AbstractTool;
import com.opengamma.core.config.impl.ConfigItem;
import com.opengamma.core.position.impl.SimplePortfolioNode;
import com.opengamma.engine.depgraph.ambiguity.FullRequirementResolution;
import com.opengamma.engine.depgraph.ambiguity.FullRequirementResolutionPrinter;
import com.opengamma.engine.depgraph.ambiguity.RequirementResolution;
import com.opengamma.engine.depgraph.ambiguity.ViewDefinitionAmbiguityTest;
import com.opengamma.engine.function.CachingFunctionRepositoryCompiler;
import com.opengamma.engine.function.CompiledFunctionDefinition;
import com.opengamma.engine.function.CompiledFunctionService;
import com.opengamma.engine.function.FunctionCompilationContext;
import com.opengamma.engine.function.config.FunctionConfigurationSource;
import com.opengamma.engine.function.exclusion.FunctionExclusionGroups;
import com.opengamma.engine.function.resolver.DefaultFunctionResolver;
import com.opengamma.engine.function.resolver.FunctionPriority;
import com.opengamma.engine.function.resolver.FunctionResolver;
import com.opengamma.engine.value.ValueSpecification;
import com.opengamma.engine.view.ViewDefinition;
import com.opengamma.financial.function.rest.RemoteFunctionConfigurationSource;
import com.opengamma.financial.tool.ToolContext;
import com.opengamma.id.VersionCorrection;
import com.opengamma.scripts.Scriptable;
import com.opengamma.transport.jaxrs.UriEndPointDescriptionProvider;
import com.opengamma.util.ClassUtils;
/**
* Tool class that compiles a view against a function repository to identify any ambiguities.
* <p>
* The configuration is fetched from the server, but the function exclusion groups and priorities must be specified manually as these are not readily available via REST interfaces (at the moment -
* there is no reason why the data couldn't be available). Any ambiguities are written to a nominated output file.
* <p>
* Note that this can be an expensive operation - very expensive if there are multiple deep ambiguities which will cause an incredibly large number of possible terminal output resolutions (based on
* the cross product of all possible inputs). It is normally only necessary to run on small portfolio samples, for example by setting {@link SimplePortfolioNode#DEBUG_FLAG}.
*/
@Scriptable
public class FindViewAmbiguities extends AbstractTool<ToolContext> {
/** Logger */
private static final Logger s_logger = LoggerFactory.getLogger(FindViewAmbiguities.class);
private static final String VIEW_NAME_OPTION = "v";
private static final String VIEW_NAME_OPTION_LONG = "view";
// -xg com.opengamma.web.spring.DemoFunctionExclusionGroupsFactoryBean
private static final String EXCLUSION_GROUPS_OPTION = "xg";
private static final String EXCLUSION_GROUPS_OPTION_LONG = "exclusionGroups";
// -fp com.opengamma.web.spring.DemoFunctionResolverFactoryBean$Priority
private static final String FUNCTION_PRIORITY_OPTION = "fp";
private static final String FUNCTION_PRIOTITY_OPTION_LONG = "functionPriority";
private static final String OUTPUT_OPTION = "o";
private static final String OUTPUT_OPTION_LONG = "output";
private static final String VERBOSE_OPTION = "f";
private static final String VERBOSE_OPTION_LONG = "full";
private final AtomicInteger _resolutions = new AtomicInteger();
private final AtomicInteger _ambiguities = new AtomicInteger();
//-------------------------------------------------------------------------
/**
* Main method to run the tool.
*
* @param args the standard tool arguments, not null
*/
public static void main(String[] args) { // CSIGNORE
new FindViewAmbiguities().invokeAndTerminate(args);
}
//-------------------------------------------------------------------------
private final class ViewDefinitionAmbiguityTestImpl extends ViewDefinitionAmbiguityTest {
private final PrintStream _out;
private FunctionResolver _functionResolver;
public ViewDefinitionAmbiguityTestImpl(final PrintStream out) {
_out = out;
}
@Override
protected FunctionCompilationContext createFunctionCompilationContext() {
final ComponentRepository repo = (ComponentRepository) getToolContext().getContextManager();
return repo.getInstance(FunctionCompilationContext.class, "main");
}
protected FunctionPriority createFunctionPrioritizer() {
// TODO: The prioritizer could be exposed over the network (sending the function identifier) and cached
final String functionPriorities = getCommandLine().getOptionValue(FUNCTION_PRIORITY_OPTION);
if (functionPriorities != null) {
try {
final Class<?> functionPrioritiesClass = ClassUtils.loadClass(functionPriorities);
Object prioritiesObject = functionPrioritiesClass.newInstance();
if (prioritiesObject instanceof InitializingBean) {
((InitializingBean) prioritiesObject).afterPropertiesSet();
}
if (prioritiesObject instanceof FactoryBean) {
prioritiesObject = ((FactoryBean<?>) prioritiesObject).getObject();
}
if (prioritiesObject instanceof FunctionPriority) {
return (FunctionPriority) prioritiesObject;
}
} catch (Exception e) {
throw new OpenGammaRuntimeException("Error loading function priorities", e);
}
}
return new FunctionPriority() {
@Override
public int getPriority(final CompiledFunctionDefinition function) {
return 0;
}
};
}
@Override
protected FunctionResolver createFunctionResolver() {
if (_functionResolver == null) {
final FunctionCompilationContext context = createFunctionCompilationContext();
final FudgeMsg configMsg = RemoteEngineContextsComponentFactory.getConfiguration(context);
final URI configUri = RemoteEngineContextsComponentFactory.getConfigurationUri(context);
final URI functionsUri;
final ExecutorService executor = Executors.newCachedThreadPool();
try {
functionsUri = UriEndPointDescriptionProvider.getAccessibleURI(executor, configUri, configMsg.getMessage("functionRepositoryConfiguration"));
} finally {
executor.shutdown();
}
s_logger.debug("Fetching remote functions from {}", functionsUri);
final FunctionConfigurationSource functions = new RemoteFunctionConfigurationSource(functionsUri);
final CompiledFunctionService compiledFunctionService = new CompiledFunctionService(functions, new CachingFunctionRepositoryCompiler(), context);
compiledFunctionService.initialize();
_functionResolver = new DefaultFunctionResolver(compiledFunctionService, createFunctionPrioritizer());
}
return _functionResolver;
}
@Override
protected FunctionExclusionGroups createFunctionExclusionGroups() {
// TODO: The exclusion groups could be exposed over the network (sending the function identifier) and cached
final String exclusionGroups = getCommandLine().getOptionValue(EXCLUSION_GROUPS_OPTION);
if (exclusionGroups != null) {
try {
final Class<?> exclusionGroupsClass = ClassUtils.loadClass(exclusionGroups);
Object groupsObject = exclusionGroupsClass.newInstance();
if (groupsObject instanceof InitializingBean) {
((InitializingBean) groupsObject).afterPropertiesSet();
}
if (groupsObject instanceof FactoryBean) {
groupsObject = ((FactoryBean<?>) groupsObject).getObject();
}
if (groupsObject instanceof FunctionExclusionGroups) {
return (FunctionExclusionGroups) groupsObject;
}
throw new IllegalArgumentException("Couldn't set exclusion groups to " + exclusionGroups + " (got " + groupsObject + ")");
} catch (Exception e) {
throw new OpenGammaRuntimeException("Error loading exclusion groups", e);
}
}
return null;
}
@Override
protected void resolved(final FullRequirementResolution resolution) {
resolvedImpl(resolution);
final int count = _resolutions.incrementAndGet();
if ((count % 100) == 0) {
s_logger.info("Checked {} resolutions", count);
}
if (resolution.isDeeplyAmbiguous() && getCommandLine().hasOption(VERBOSE_OPTION)) {
synchronized (this) {
(new FullRequirementResolutionPrinter(_out)).print(resolution);
}
}
}
protected void resolvedImpl(final FullRequirementResolution resolution) {
super.resolved(resolution);
}
@Override
protected synchronized void directAmbiguity(final FullRequirementResolution resolution) {
final int count = _ambiguities.incrementAndGet();
if ((count % 10) == 0) {
s_logger.info("Found {} ambiguities", count);
}
_out.println(resolution.getRequirement());
for (Collection<RequirementResolution> nestedResolutions : resolution.getResolutions()) {
final List<String> functions = new ArrayList<String>();
final List<ValueSpecification> specifications = new ArrayList<ValueSpecification>();
boolean failure = false;
for (RequirementResolution nestedResolution : nestedResolutions) {
if (nestedResolution != null) {
functions.add(nestedResolution.getFunction().getFunctionId());
specifications.add(nestedResolution.getSpecification());
} else {
failure = true;
}
}
for (String function : functions) {
_out.println("\t" + function);
}
if (failure) {
_out.println("\t+ failure(s)");
}
for (ValueSpecification specification : specifications) {
_out.println("\t" + specification);
}
_out.println();
}
}
@Override
protected synchronized void deepAmbiguity(final FullRequirementResolution resolution) {
for (Collection<RequirementResolution> nestedResolutions : resolution.getResolutions()) {
for (RequirementResolution nestedResolution : nestedResolutions) {
for (FullRequirementResolution inputResolution : nestedResolution.getInputs()) {
resolvedImpl(inputResolution);
}
}
}
_out.println(resolution.getRequirement());
for (Collection<RequirementResolution> nestedResolutions : resolution.getResolutions()) {
for (RequirementResolution nestedResolution : nestedResolutions) {
_out.println("\t" + nestedResolution.getSpecification());
}
}
_out.println();
}
}
private static Option createViewNameOption() {
final Option option = new Option(VIEW_NAME_OPTION, VIEW_NAME_OPTION_LONG, true, "the view to check, omit to check all");
option.setArgName("view");
option.setRequired(false);
return option;
}
private static Option createFunctionExclusionGroupsBeanOption() {
final Option option = new Option(EXCLUSION_GROUPS_OPTION, EXCLUSION_GROUPS_OPTION_LONG, true, "the exclusion groups to use");
option.setArgName("class");
option.setRequired(false);
return option;
}
private static Option createFunctionPrioritiesOption() {
final Option option = new Option(FUNCTION_PRIORITY_OPTION, FUNCTION_PRIOTITY_OPTION_LONG, true, "the function prioritizer to use");
option.setArgName("class");
option.setRequired(false);
return option;
}
private static Option createOutputOption() {
final Option option = new Option(OUTPUT_OPTION, OUTPUT_OPTION_LONG, true, "the output file to write");
option.setArgName("filename");
option.setRequired(false);
return option;
}
private static Option createVerboseOption() {
final Option option = new Option(VERBOSE_OPTION, VERBOSE_OPTION_LONG, false, "whether to write out ambiguities in full");
option.setRequired(false);
return option;
}
private static PrintStream openStream(final String filename) throws IOException {
if ((filename == null) || "stdout".equals(filename)) {
return System.out;
} else if ("stderr".equals(filename)) {
return System.err;
} else {
return new PrintStream(new FileOutputStream(filename));
}
}
// AbstractTool
@Override
protected void doRun() throws Exception {
final PrintStream out = openStream(getCommandLine().getOptionValue(OUTPUT_OPTION));
final ViewDefinitionAmbiguityTest test = new ViewDefinitionAmbiguityTestImpl(out);
final String viewName = getCommandLine().getOptionValue(VIEW_NAME_OPTION);
int count = 0;
if (viewName != null) {
s_logger.info("Testing {}", viewName);
final ViewDefinition viewDefinition = getToolContext().getConfigSource().getLatestByName(ViewDefinition.class, viewName);
if (viewDefinition == null) {
throw new IllegalArgumentException("View definition " + viewName + " not found");
}
out.println("View = " + viewName);
test.runAmbiguityTest(viewDefinition);
count++;
} else {
final Collection<ConfigItem<ViewDefinition>> viewDefinitions = getToolContext().getConfigSource().getAll(ViewDefinition.class, VersionCorrection.LATEST);
s_logger.info("Testing {} view definition(s)", viewDefinitions.size());
for (ConfigItem<ViewDefinition> viewDefinitionConfig : viewDefinitions) {
final ViewDefinition viewDefinition = viewDefinitionConfig.getValue();
s_logger.info("Testing {}", viewDefinition.getName());
out.println("View = " + viewDefinition.getName());
final int resolutions = _resolutions.get();
final int ambiguities = _ambiguities.get();
test.runAmbiguityTest(viewDefinition);
count++;
out.println("Resolutions = " + (_resolutions.get() - resolutions));
out.println("Ambiguities = " + (_ambiguities.get() - ambiguities));
}
}
s_logger.info("{} view(s) tested", count);
out.println("Total resolutions = " + _resolutions.get());
out.println("Total ambiguities = " + _ambiguities.get());
out.close();
}
@Override
protected Options createOptions(final boolean mandatoryConfigResource) {
final Options options = super.createOptions(mandatoryConfigResource);
options.addOption(createViewNameOption());
options.addOption(createFunctionExclusionGroupsBeanOption());
options.addOption(createFunctionPrioritiesOption());
options.addOption(createOutputOption());
options.addOption(createVerboseOption());
return options;
}
}