/**
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.sesame.engine;
import java.util.EnumSet;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.codahale.metrics.MetricFilter;
import com.codahale.metrics.MetricRegistry;
import com.google.common.base.Optional;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.ImmutableSet;
import com.opengamma.core.position.PositionOrTrade;
import com.opengamma.core.security.Security;
import com.opengamma.sesame.cache.CacheInvalidator;
import com.opengamma.sesame.cache.CacheProvider;
import com.opengamma.sesame.config.FunctionModelConfig;
import com.opengamma.sesame.config.ViewConfig;
import com.opengamma.sesame.function.AvailableImplementations;
import com.opengamma.sesame.function.AvailableOutputs;
import com.opengamma.sesame.graph.FunctionBuilder;
import com.opengamma.util.ArgumentChecker;
/**
* Factory for creating instances of {@link View}.
* This is one of the key classes of the calculation engine. The {@link #createView} methods take a view configuration
* and returns a view that is ready to be executed.
* <p>
* Each view factory contains a cache which is shared by all views it creates. Each view requests a cache at the
* start of a calculation cycle and uses it for the duration of the cycle. If {@link #clearCache()} is invoked
* the cache in the view factory is replaced with a new, empty cache. When each view starts its next calculation
* cycle it will request a cache and be given the new one. The previous cache is unchanged so any views that
* are still using it are unaffected.
*/
public class ViewFactory implements ViewFactoryMonitor {
private static final Logger s_logger = LoggerFactory.getLogger(ViewFactory.class);
private final ExecutorService _executor;
private final AvailableOutputs _availableOutputs;
private final AvailableImplementations _availableImplementations;
private final EnumSet<FunctionService> _defaultServices;
private final FunctionModelConfig _defaultConfig;
private final FunctionBuilder _functionBuilder = new FunctionBuilder();
/**
* Reference to the current cache. When {@link #clearCache()} is called this reference is updated to point
* to a new, empty cache. This means the new cache will be provided to views through {@link #_cacheProvider}
* at the start of their next calculation cycle but any views using the existing cache wil be unaffected.
*/
private final AtomicReference<Cache<Object, Object>> _cacheRef;
/**
* Provides a cache to views. Views request a cache at the start of each calculation cycle and use it for
* the duration of that cycle. This allows the cache in the view factory to change without any effect
* on running views.
*/
private final CacheProvider _cacheProvider = new CacheProvider() {
@Override
public Cache<Object, Object> get() {
return _cacheRef.get();
}
};
/** For building new caches. A new cache is created whenever data in the existing cache becomes invalid. */
private final CacheBuilder<Object, Object> _cacheBuilder;
private final Optional<MetricRegistry> _metricRegistry;
private final ComponentMap _componentMap;
private final CacheInvalidator _cacheInvalidator;
public ViewFactory(ExecutorService executor,
ComponentMap componentMap,
AvailableOutputs availableOutputs,
AvailableImplementations availableImplementations,
FunctionModelConfig defaultConfig,
EnumSet<FunctionService> defaultServices,
CacheBuilder<Object, Object> cacheBuilder,
CacheInvalidator cacheInvalidator,
Optional<MetricRegistry> metricRegistry) {
_availableOutputs = ArgumentChecker.notNull(availableOutputs, "availableOutputs");
_availableImplementations = ArgumentChecker.notNull(availableImplementations, "availableImplementations");
_defaultServices = ArgumentChecker.notNull(defaultServices, "defaultServices");
_defaultConfig = ArgumentChecker.notNull(defaultConfig, "defaultConfig");
_executor = ArgumentChecker.notNull(executor, "executor");
_cacheBuilder = ArgumentChecker.notNull(cacheBuilder, "cacheBuilder");
_componentMap = ArgumentChecker.notNull(componentMap, "componentMap");
_cacheInvalidator = ArgumentChecker.notNull(cacheInvalidator, "cacheInvalidator");
// create an initial empty cache
_cacheRef = new AtomicReference<>(_cacheBuilder.<Object, Object>build());
_metricRegistry = ArgumentChecker.notNull(metricRegistry, "metricRegistry");
}
/**
* Currently the inputs must be instances of {@link PositionOrTrade} or {@link Security}.
* This will be relaxed in future.
*
* @param viewConfig the configuration to use, not null
* @param inputTypes the types of the inputs to the calculations, e.g. trades, positions, securities
* @return the view, not null
*/
public View createView(ViewConfig viewConfig, Set<Class<?>> inputTypes) {
return createView(viewConfig, _defaultServices, inputTypes);
}
/**
* Currently the inputs must be instances of {@link PositionOrTrade} or {@link Security}.
* This will be relaxed in future.
*
* @param viewConfig the configuration to use, not null
* @param inputTypes the types of the inputs to the calculations, e.g. trades, positions, securities
* @return the view, not null
*/
public View createView(ViewConfig viewConfig, Class<?>... inputTypes) {
return createView(viewConfig, _defaultServices, inputTypes);
}
/**
* Currently the inputs must be instances of {@link PositionOrTrade} or {@link Security}.
* This will be relaxed in future.
*
* @param viewConfig the configuration to use, not null
* @param services the services to run, not null
* @param inputTypes the types of the inputs to the calculations, e.g. trades, positions, securities
* @return the view, not null
*/
public View createView(ViewConfig viewConfig, EnumSet<FunctionService> services, Class<?>... inputTypes) {
return createView(viewConfig, services, ImmutableSet.copyOf(inputTypes));
}
/**
* Currently the inputs must be instances of {@link PositionOrTrade} or {@link Security}.
* This will be relaxed in future.
*
* @param viewConfig the configuration to use, not null
* @param services the services to run, not null
* @param inputTypes the types of the inputs to the calculations, e.g. trades, positions, securities
* @return the view, not null
*/
public View createView(ViewConfig viewConfig, EnumSet<FunctionService> services, Set<Class<?>> inputTypes) {
return new View(viewConfig, _executor, _defaultConfig, _functionBuilder, services,
_componentMap, inputTypes, _availableOutputs, _availableImplementations,
_cacheProvider, _cacheBuilder, _cacheInvalidator, _metricRegistry);
}
/**
* Clears all entries from the cache.
* <p>
* This doesn't affect the caches of any running views, it simply replaces the current cache with an empty one
* so when each view starts its next cycle it gets the new cache.
*/
public void clearCache() {
s_logger.info("Clearing cache");
_cacheRef.set(_cacheBuilder.<Object, Object>build());
}
@Override
public void clearMetrics() {
if (_metricRegistry.isPresent()) {
s_logger.info("Clearing metrics");
_metricRegistry.get().removeMatching(MetricFilter.ALL);
}
}
}