/** * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.livedata.server; import java.util.Collection; import java.util.HashSet; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.opengamma.livedata.LiveDataSpecification; import com.opengamma.livedata.client.Heartbeater; import com.opengamma.livedata.server.distribution.MarketDataDistributor; import com.opengamma.util.async.AbstractHousekeeper; /** * Keeps track of all market data currently being published, and controls the expiry by keeping track of heartbeat messages. */ public class ExpirationManager extends AbstractHousekeeper<StandardLiveDataServer> { /** * How long market data should live, by default. Milliseconds */ public static final long DEFAULT_TIMEOUT_EXTENSION = 3 * Heartbeater.DEFAULT_PERIOD; /** * How often expiry task should run. Milliseconds */ public static final long DEFAULT_CHECK_PERIOD = Heartbeater.DEFAULT_PERIOD / 2; /** Logger. */ private static final Logger s_logger = LoggerFactory.getLogger(ExpirationManager.class); /** * The extension to the timeout. */ private long _timeoutExtension = DEFAULT_TIMEOUT_EXTENSION; /** * The checking period. */ private long _checkPeriod = DEFAULT_CHECK_PERIOD; /** * Creates the manager with a default period between checks. * * @param dataServer the live data server, not null */ /* package */ExpirationManager(StandardLiveDataServer dataServer) { super(dataServer); } /** * Sets the timeout extension. * * @param timeoutExtension the extension in milliseconds */ public void setTimeoutExtension(final long timeoutExtension) { _timeoutExtension = timeoutExtension; } /** * Sets the check period. This must be set before the manager is started. * * @param checkPeriod the checking period in milliseconds */ public void setCheckPeriod(final long checkPeriod) { _checkPeriod = checkPeriod; } /** * Gets the check period. * * @return the checking period in milliseconds */ public long getCheckPeriod() { return _checkPeriod; } /** * Gets the timeout extension. * * @return the timeoutExtension the extension in milliseconds */ public long getTimeoutExtension() { return _timeoutExtension; } /** * Extends the timeout for the live data specification. * * @param fullyQualifiedSpecs the fully-qualified specifications, not null */ public void extendPublicationTimeout(Collection<LiveDataSpecification> fullyQualifiedSpecs) { final StandardLiveDataServer server = getTarget(); if (server == null) { s_logger.warn("No live data server set in expiration manager - unable to extend publication timeouts"); return; } Set<LiveDataSpecification> resubscriptions = new HashSet<LiveDataSpecification>(); for (LiveDataSpecification fullyQualifiedSpec : fullyQualifiedSpecs) { MarketDataDistributor distributor = server.getMarketDataDistributor(fullyQualifiedSpec); if (distributor != null) { s_logger.debug("Heartbeat on {}", fullyQualifiedSpec); distributor.extendExpiry(getTimeoutExtension()); } else { // We have (presumably erroneously) dropped a subscription that a client is // expecting. In lieu of determining the underlying cause of dropping the // subscription, we automatically create a new subscription s_logger.info("Failed to find distributor for heartbeat on {} from {} - will resubscribe", fullyQualifiedSpec, server); resubscriptions.add(fullyQualifiedSpec); } } if (!resubscriptions.isEmpty()) { s_logger.warn("Received heartbeat for {} live data specifications. Resubscribing to {} of these.", fullyQualifiedSpecs.size(), resubscriptions.size()); server.subscribe(resubscriptions, false); } } @Override protected int getPeriodSeconds() { return (int) (_checkPeriod / 1000); } @Override protected boolean housekeep(StandardLiveDataServer server) { s_logger.debug("Checking for data specifications to time out"); int nExpired = server.expireSubscriptions(); s_logger.info("Expired {} specifications", nExpired); return server.isRunning(); } }