/**
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.core.historicaltimeseries.impl;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.threeten.bp.LocalDate;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.id.ExternalId;
import com.opengamma.id.UniqueId;
import com.opengamma.timeseries.date.localdate.LocalDateDoubleTimeSeries;
import com.opengamma.util.ArgumentChecker;
/**
* An extremely minimal and lightweight {@code HistoricalTimeSeriesSource} that pulls data
* directly from Redis for the purpose of historical simulations, where the full market data
* series for that simulation can change every day..
* This is <em>only</em> appropriate for use in conjunction with {@code HistoricalTimeSeriesFunction}
* and requires its own specific API for publishing data. It is <strong>not</strong>
* a general purpose component.
* <p>
* Effectively, there is a double-time series involved:
* <ul>
* <li>The {@code SimulationExecution} series is one time series, representing the date
* that the simulation series was performed (and/or expected to be used).</li>
* <li>The {@code Value} series is one point inside a particular simulation.</li>
* </ul>
* So, for example, assume that every day a system generates a whole new time series,
* where that time series is the simulation points that should be run. In that case,
* this class may be appropriate.
* <p>
* The following constraints must hold for this Source to be of any utility whatsoever:
* <ul>
* <li>Historical lookups are not required. Because they are not supported.</li>
* <li>Version corrections are not required. Because they are not supported.</li>
* <li>Each time series has a <b>single</b> {@link ExternalId} which then acts
* as the {@link UniqueId} internally.</li>
* <li>Each external ID has a single time series (thus there is not the capacity to store
* different Data Source, Data Provider, Observation Time, Data Field series).</li>
* </ul>
* <p>
* Where a method is not supported semantically, an {@link UnsupportedOperationException}
* will be thrown. Where use indicates that this class may be being used incorrectly,
* a log message will be written at {@code WARN} level.
* <p>
* See <a href="http://jira.opengamma.com/browse/PLAT-3385">PLAT-3385</a> for the original
* requirement.
*/
public class RedisSimulationSeriesSource extends NonVersionedRedisHistoricalTimeSeriesSource implements SimulationSeriesSource {
private static final Logger s_logger = LoggerFactory.getLogger(RedisSimulationSeriesSource.class);
private LocalDate _currentSimulationExecutionDate = LocalDate.now();
public RedisSimulationSeriesSource(JedisPool jedisPool) {
this(jedisPool, "");
}
public RedisSimulationSeriesSource(JedisPool jedisPool, String redisPrefix) {
super(jedisPool, redisPrefix, "RedisSimulationSeriesSource");
}
@Override
public RedisSimulationSeriesSource withSimulationDate(LocalDate date) {
RedisSimulationSeriesSource redisSimulationSeriesSource = new RedisSimulationSeriesSource(getJedisPool(), getRedisPrefix());
redisSimulationSeriesSource.setCurrentSimulationExecutionDate(date);
return redisSimulationSeriesSource;
}
/**
* Gets the currentSimulationExecutionDate.
* @return the currentSimulationExecutionDate
*/
@Override
public LocalDate getCurrentSimulationExecutionDate() {
return _currentSimulationExecutionDate;
}
/**
* Sets the currentSimulationExecutionDate.
* This will be used in calls to load the simulation series.
* @param currentSimulationExecutionDate the currentSimulationExecutionDate
*/
public void setCurrentSimulationExecutionDate(LocalDate currentSimulationExecutionDate) {
_currentSimulationExecutionDate = currentSimulationExecutionDate;
}
// ------------------------------------------------------------------------
// REDIS MANIPULATION OPERATIONS:
// ------------------------------------------------------------------------
@Override
public void updateTimeSeriesPoint(UniqueId uniqueId, LocalDate simulationExecutionDate, LocalDate valueDate, double value) {
ArgumentChecker.notNull(uniqueId, "uniqueId");
ArgumentChecker.notNull(simulationExecutionDate, "simulationExecutionDate");
ArgumentChecker.notNull(valueDate, "valueDate");
String redisKey = toRedisKey(uniqueId, simulationExecutionDate);
updateTimeSeriesPoint(redisKey, valueDate, value);
}
@Override
public void updateTimeSeries(UniqueId uniqueId, LocalDate simulationExecutionDate, LocalDateDoubleTimeSeries timeseries) {
ArgumentChecker.notNull(uniqueId, "uniqueId");
ArgumentChecker.notNull(simulationExecutionDate, "simulationExecutionDate");
ArgumentChecker.notNull(timeseries, "timeseries");
String redisKey = toRedisKey(uniqueId, simulationExecutionDate);
updateTimeSeries(redisKey, timeseries, false);
}
@Override
public void replaceTimeSeries(UniqueId uniqueId, LocalDate simulationExecutionDate, LocalDateDoubleTimeSeries timeSeries) {
ArgumentChecker.notNull(uniqueId, "uniqueId");
ArgumentChecker.notNull(timeSeries, "timeSeries");
String redisKey = toRedisKey(uniqueId, simulationExecutionDate);
updateTimeSeries(redisKey, timeSeries, true);
}
@Override
public void clearExecutionDate(LocalDate simulationExecutionDate) {
final String keysPattern = getRedisPrefix() + "*_" + simulationExecutionDate.toString();
Jedis jedis = getJedisPool().getResource();
try {
Set<String> keys = jedis.keys(keysPattern);
if (!keys.isEmpty()) {
jedis.del(keys.toArray(new String[0]));
}
getJedisPool().returnResource(jedis);
} catch (Exception e) {
s_logger.error("Unable to clear execution date " + simulationExecutionDate, e);
getJedisPool().returnBrokenResource(jedis);
throw new OpenGammaRuntimeException("Unable to clear execution date " + simulationExecutionDate, e);
}
}
@Override
protected String toRedisKey(UniqueId uniqueId) {
return toRedisKey(uniqueId, getCurrentSimulationExecutionDate());
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.equals(o)) {
return false;
}
RedisSimulationSeriesSource that = (RedisSimulationSeriesSource) o;
return _currentSimulationExecutionDate.equals(that._currentSimulationExecutionDate);
}
@Override
public int hashCode() {
int result = super.hashCode();
return 31 * result + _currentSimulationExecutionDate.hashCode();
}
}