Package com.opengamma.bbg.livedata

Source Code of com.opengamma.bbg.livedata.BloombergLiveDataServer$ConnectTask

/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.bbg.livedata;

import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;

import org.fudgemsg.FudgeMsg;
import org.fudgemsg.mapping.FudgeSerializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.Lifecycle;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.bloomberglp.blpapi.Event;
import com.bloomberglp.blpapi.Message;
import com.bloomberglp.blpapi.MessageIterator;
import com.bloomberglp.blpapi.Subscription;
import com.bloomberglp.blpapi.SubscriptionList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.bbg.BloombergConnector;
import com.opengamma.bbg.BloombergConstants;
import com.opengamma.bbg.SessionProvider;
import com.opengamma.bbg.referencedata.ReferenceDataProvider;
import com.opengamma.bbg.util.BloombergDataUtils;
import com.opengamma.core.id.ExternalSchemes;
import com.opengamma.engine.marketdata.live.MarketDataAvailabilityNotification;
import com.opengamma.livedata.server.AbstractEventDispatcher;
import com.opengamma.transport.FudgeMessageSender;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.fudgemsg.OpenGammaFudgeContext;

import net.sf.ehcache.CacheManager;

/**
* A Bloomberg Live Data Server.
*/
public class BloombergLiveDataServer extends AbstractBloombergLiveDataServer {

  /** Logger. */
  private static final Logger s_logger = LoggerFactory.getLogger(BloombergLiveDataServer.class);
  /** Interval between attempts to reconnect to Bloomberg. */
  private static final long RECONNECT_PERIOD = 30000;

  // Injected Inputs:
  private final BloombergConnector _bloombergConnector;
  private final ReferenceDataProvider _referenceDataProvider;

  /** Creates and manages the Bloomberg session and service. */
  private final SessionProvider _sessionProvider;
  /** Timer for managing reconnection tasks. */
  private final Timer _timer = new Timer();

  // Runtime State:
  private BloombergEventDispatcher _eventDispatcher;
  private Thread _eventDispatcherThread;

  private long _subscriptionLimit = Long.MAX_VALUE;
  private volatile RejectedDueToSubscriptionLimitEvent _lastLimitRejection; // = null
  /** Task for (re)connecting to Bloomberg. */
  private BloombergLiveDataServer.ConnectTask _connectTask;
  /** For sending a notification message that Bloomberg data is available. */
  private final FudgeMessageSender _availabilityNotificationSender;

  /**
   * Creates an instance.
   *
   * @param bloombergConnector  the connector, not null
   * @param referenceDataProvider  the reference data provider, not null
   * @param cacheManager  the cache manager, not null
   * @param availabilityNotificationSender For sending notifications when Bloomberg data becomes available
   */
  public BloombergLiveDataServer(BloombergConnector bloombergConnector,
                                 ReferenceDataProvider referenceDataProvider,
                                 CacheManager cacheManager,
                                 FudgeMessageSender availabilityNotificationSender) {
    super(cacheManager);
    ArgumentChecker.notNull(bloombergConnector, "bloombergConnector");
    ArgumentChecker.notNull(referenceDataProvider, "referenceDataProvider");
    ArgumentChecker.notNull(availabilityNotificationSender, "availabilityNotificationSender");
    _availabilityNotificationSender = availabilityNotificationSender;
    _bloombergConnector = bloombergConnector;
    _referenceDataProvider = referenceDataProvider;
    _sessionProvider = new SessionProvider(_bloombergConnector, BloombergConstants.MKT_DATA_SVC_NAME);
  }

  //-------------------------------------------------------------------------
  /**
   * Gets the Bloomberg connector.
   *
   * @return the connector, not null
   */
  public BloombergConnector getBloombergConnector() {
    return _bloombergConnector;
  }

  @Override
  protected void doDisconnect() {
    _eventDispatcher.terminate();
    try {
      _eventDispatcherThread.join(10000L);
    } catch (InterruptedException e) {
      Thread.interrupted();
      s_logger.warn("Interrupted while waiting for event dispatcher thread to terminate", e);
    }
    _eventDispatcher = null;
    _eventDispatcherThread = null;
    _sessionProvider.invalidateSession();
  }

  @Override
  protected void doConnect() {
    // getting the session throws an exception if BBG isn't available which is the behaviour we want
    _sessionProvider.getSession();
    BloombergEventDispatcher eventDispatcher = new BloombergEventDispatcher(this);
    Thread eventDispatcherThread = new Thread(eventDispatcher, "Bloomberg LiveData Dispatcher");
    eventDispatcherThread.setDaemon(true);
    eventDispatcherThread.start();

    // If we got this far, we're ready, and we can call all the setters.
    _eventDispatcher = eventDispatcher;
    _eventDispatcherThread = eventDispatcherThread;

    // make sure the reference data provider also reconnects
    if (_referenceDataProvider instanceof Lifecycle) {
      ((Lifecycle) _referenceDataProvider).start();
    }
  }

  @Override
  protected void checkSubscribe(Set<String> uniqueIds) {
    //NOTE: need to do this here, rather than in doSubscribe, because otherwise we'd do the initial snapshot anyway.
    checkLimitRemaining(uniqueIds.size());
    super.checkSubscribe(uniqueIds);
  }

  @Override
  protected Map<String, Object> doSubscribe(Collection<String> bbgUniqueIds) {
    ArgumentChecker.notNull(bbgUniqueIds, "Unique IDs");
    if (bbgUniqueIds.isEmpty()) {
      return Collections.emptyMap();
    }

    Map<String, Object> returnValue = Maps.newHashMap();
   
    SubscriptionList sl = new SubscriptionList();
    for (String bbgUniqueId : bbgUniqueIds) {
      String securityDes = getBloombergSubscriptionPathPrefix() + bbgUniqueId;
      Subscription subscription = new Subscription(securityDes, BloombergDataUtils.STANDARD_FIELDS_LIST);
      sl.add(subscription);
      returnValue.put(bbgUniqueId, subscription);
    }
   
    try {
      _sessionProvider.getSession().subscribe(sl);
    } catch (Exception e) {
      throw new OpenGammaRuntimeException("Could not subscribe to " + bbgUniqueIds, e);
    }
   
    return returnValue;
  }

  public long getSubscriptionLimit() {
    return _subscriptionLimit;
  }

  public void setSubscriptionLimit(long subscriptionLimit) {
    _subscriptionLimit = subscriptionLimit;
  }
 
  private void checkLimitRemaining(int requested) {
    int afterSubscriptionCount = requested + getActiveSubscriptionIds().size();
    if (afterSubscriptionCount > getSubscriptionLimit()) {
      String message = "Rejecting subscription request, would result in limit of " + getSubscriptionLimit() + " being exceeded " + afterSubscriptionCount;
      s_logger.warn(message);
      _lastLimitRejection = new RejectedDueToSubscriptionLimitEvent(getSubscriptionLimit(), requested, afterSubscriptionCount);
      throw new OpenGammaRuntimeException(message);
    }
  }
 
  @Override
  protected void doUnsubscribe(Collection<Object> subscriptionHandles) {
    ArgumentChecker.notNull(subscriptionHandles, "Subscription handles");
    if (subscriptionHandles.isEmpty()) {
      return;
    }
   
    SubscriptionList sl = new SubscriptionList();
   
    for (Object subscriptionHandle : subscriptionHandles) {
      Subscription subscription = (Subscription) subscriptionHandle;
      sl.add(subscription);
    }
   
    try {
      _sessionProvider.getSession().unsubscribe(sl);
    } catch (Exception e) {
      throw new OpenGammaRuntimeException("Could not unsubscribe from " + subscriptionHandles, e);
    }
  }

  @Override
  public ReferenceDataProvider getReferenceDataProvider() {
    return _referenceDataProvider;
  }

  /**
   * Gets the last limit rejection event.
   * @return the lastLimitRejection
   */
  public RejectedDueToSubscriptionLimitEvent getLastLimitRejection() {
    return _lastLimitRejection;
  }

  @Override
  public synchronized void start() {
    if (getConnectionStatus() == ConnectionStatus.NOT_CONNECTED) {
      _connectTask = new ConnectTask();
      _timer.schedule(_connectTask, 0, RECONNECT_PERIOD);
    }
  }

  @Override
  public synchronized void stop() {
    if (getConnectionStatus() == ConnectionStatus.CONNECTED) {
      stopExpirationManager();
      if (_connectTask != null) {
        _connectTask.cancel();
      }
      disconnect();
    }
  }

  /**
   * Task that connects to Bloomberg and periodically tries to reconnect if the connection is down.
   */
  private class ConnectTask extends TimerTask {

    @Override
    public void run() {
      synchronized (BloombergLiveDataServer.this) {
        if (getConnectionStatus() == ConnectionStatus.NOT_CONNECTED) {
          try {
            s_logger.info("Connecting to Bloomberg");
            connect();
            startExpirationManager();
            reestablishSubscriptions();
            MarketDataAvailabilityNotification notification = new MarketDataAvailabilityNotification(
                ImmutableSet.of(
                    ExternalSchemes.BLOOMBERG_BUID,
                    ExternalSchemes.BLOOMBERG_BUID_WEAK,
                    ExternalSchemes.BLOOMBERG_TCM,
                    ExternalSchemes.BLOOMBERG_TICKER,
                    ExternalSchemes.BLOOMBERG_TICKER_WEAK));
            FudgeSerializer serializer = new FudgeSerializer(OpenGammaFudgeContext.getInstance());
            s_logger.info("Sending notification that Bloomberg is available: {}", notification);
            _availabilityNotificationSender.send(notification.toFudgeMsg(serializer));
          } catch (Exception e) {
            s_logger.warn("Failed to connect to Bloomberg", e);
          }
        }
      }
    }
  }

  /**
   * Starts the Bloomberg Server.
   *
   * @param args Not needed
   */
  public static void main(String[] args) { // CSIGNORE
    ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("/com/opengamma/bbg/livedata/bbg-livedata-context.xml");
    context.start();
  }
  /**
   * This is the job which actually will dispatch messages from Bloomberg to the various
   * internal consumers.
   *
   * @author kirk
   */
  private final class BloombergEventDispatcher extends AbstractEventDispatcher {

    private BloombergEventDispatcher(BloombergLiveDataServer server) {
      super(server);
    }

    @Override
    protected void preStart() {
      super.preStart();
    }

    @Override
    protected void dispatch(long maxWaitMilliseconds) {
      Event event = null;
      try {
        event = _sessionProvider.getSession().nextEvent(1000L);
      } catch (InterruptedException e) {
        Thread.interrupted();
      }
      if (event == null) {
        return;
      }
      MessageIterator msgIter = event.messageIterator();
      while (msgIter.hasNext()) {
        Message msg = msgIter.next();
        String bbgUniqueId = msg.topicName();

        if (event.eventType() == Event.EventType.SUBSCRIPTION_DATA) {
          FudgeMsg eventAsFudgeMsg = BloombergDataUtils.parseElement(msg.asElement());
          liveDataReceived(bbgUniqueId, eventAsFudgeMsg);
          // REVIEW 2012-09-19 Andrew -- Why return? Might the event contain multiple messages?
          return;
        }
        s_logger.info("Got event {} {} {}", event.eventType(), bbgUniqueId, msg.asElement());

        if (event.eventType() == Event.EventType.SESSION_STATUS) {
          s_logger.info("SESSION_STATUS event received: {}", msg.messageType());
          if (msg.messageType().toString().equals("SessionTerminated")) {
            disconnect();
            terminate();
          }
        }
      }
    }
  }
}
TOP

Related Classes of com.opengamma.bbg.livedata.BloombergLiveDataServer$ConnectTask

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.