Package com.opengamma.livedata.client

Source Code of com.opengamma.livedata.client.SubscriptionHandle

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

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.opengamma.livedata.LiveDataListener;
import com.opengamma.livedata.LiveDataSpecification;
import com.opengamma.livedata.LiveDataValueUpdate;
import com.opengamma.livedata.LiveDataValueUpdateBean;
import com.opengamma.livedata.UserPrincipal;
import com.opengamma.livedata.msg.LiveDataSubscriptionResponse;
import com.opengamma.livedata.msg.LiveDataSubscriptionResult;
import com.opengamma.livedata.msg.SubscriptionType;
import com.opengamma.util.ArgumentChecker;

/**
* A subscription handle is kept by the client while a subscription is being established.
* After the subscription has been established, it is no longer needed.
*
* @author kirk
*/
public class SubscriptionHandle {
 
  private static final Logger s_logger = LoggerFactory.getLogger(SubscriptionHandle.class);
 
  private final UserPrincipal _user;
  private final SubscriptionType _subscriptionType;
  private final LiveDataSpecification _requestedSpecification;
  private final LiveDataListener _listener;
  private final List<LiveDataValueUpdateBean> _ticksOnHold = new ArrayList<LiveDataValueUpdateBean>();
  private LiveDataValueUpdateBean _snapshotOnHold; // = null;
 
  public SubscriptionHandle(
      UserPrincipal user,
      SubscriptionType subscriptionType,
      LiveDataSpecification requestedSpecification,
      LiveDataListener listener) {
    ArgumentChecker.notNull(user, "User credentials");
    ArgumentChecker.notNull(subscriptionType, "Subscription type");
    ArgumentChecker.notNull(requestedSpecification, "Requested Specification");
    ArgumentChecker.notNull(listener, "Live Data Listener");
    _user = user;
    _subscriptionType = subscriptionType;
    _requestedSpecification = requestedSpecification;
    _listener = listener;
  }

  /**
   * @return the user principal
   */
  public UserPrincipal getUser() {
    return _user;
  }
 
  public SubscriptionType getSubscriptionType() {
    return _subscriptionType;
  }

  /**
   * @return the requestedSpecification
   */
  public LiveDataSpecification getRequestedSpecification() {
    return _requestedSpecification;
  }

  /**
   * @return the listener
   */
  public LiveDataListener getListener() {
    return _listener;
  }

  @Override
  public String toString() {
    return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
  }
 
  /**
   * Informs the client listener about the response received from the server
   *
   * @param response Response received, not null
   */
  public void subscriptionResultReceived(LiveDataSubscriptionResponse response) {
    if (s_logger.isDebugEnabled()) {
      if (_subscriptionType == SubscriptionType.SNAPSHOT) {
        if (response.getSubscriptionResult() == LiveDataSubscriptionResult.SUCCESS) {
          s_logger.debug("Got snapshot {}", getRequestedSpecification());
        } else {
          s_logger.debug("Failed to snapshot {}. Result was {}, msg = {}",
              new Object[] {getRequestedSpecification(), response.getSubscriptionResult(), response.getUserMessage() });
        }
      } else {
        if (response.getSubscriptionResult() == LiveDataSubscriptionResult.SUCCESS) {
          s_logger.debug("Established subscription to {}", getRequestedSpecification());
        } else if (response.getSubscriptionResult() == LiveDataSubscriptionResult.INTERNAL_ERROR) {
          s_logger.debug("Failed to establish subscription, {} {}, request = {}",
              new Object[] {response.getSubscriptionResult(), response.getUserMessage(), getRequestedSpecification() });
        } else {
          s_logger.debug("Failed to establish subscription, {} {}, request = {}",
              new Object[] {response.getSubscriptionResult(), response.getUserMessage(), getRequestedSpecification() });
        }
      }
    }
   
    getListener().subscriptionResultReceived(response);
  }
 
  /**
   * In a two-phase subscription procedure (see LIV-18), after a subscription is established,
   * the client needs to get a snapshot from the server.
   * Between establishing the subscription and getting the snapshot, all ticks
   * must be kept in memory and only released after the snapshot is received.
   *
   * @param tick Tick to add to temporary memory store
   */
  public synchronized void addTickOnHold(LiveDataValueUpdateBean tick) {
    _ticksOnHold.add(tick);
  }
 
  /**
   * In a two-phase subscription procedure (see LIV-18), after a subscription is established,
   * the client needs to get a snapshot from the server. This method is used
   * to store that snapshot.
   *
   * @param snapshot The snapshot to be placed on hold
   */
  public synchronized void addSnapshotOnHold(LiveDataValueUpdateBean snapshot) {
    if (_snapshotOnHold != null) {
      throw new IllegalStateException("Snapshot has already been set");
    }
   
    _snapshotOnHold = snapshot;
  }
 
  /**
   * Releases the snapshot and ticks stored in memory.
   * For an explanation of why we need to do this, see LIV-18.
   * The method copes with server restarts during the subscription process
   * by assuming that the server sends a full image to the client
   * when it restarts.
   */
  public synchronized void releaseTicksOnHold() {
    if (_snapshotOnHold == null) {
      // this will happen if the snapshot failed.
      s_logger.debug("No ticks to send to {}. {}", getListener(), getRequestedSpecification());
      return;
    }
   
    long snapshotSequenceNo = _snapshotOnHold.getSequenceNumber();
   
    // Find the LAST reset (in theory, there could be multiple resets although
    // this is a highly theoretical case)
    Integer resetIndex = null;
    for (int i = 0; i < _ticksOnHold.size(); i++) {
      LiveDataValueUpdateBean tick = _ticksOnHold.get(i);
      if (tick.getSequenceNumber() == LiveDataValueUpdate.SEQUENCE_START) {
        resetIndex = i;               
      }
    }
   
    if (resetIndex == null) {
      s_logger.debug("{}: Sending snapshot and {} ticks on hold to {}",
          new Object[] {getRequestedSpecification(), _ticksOnHold.size(), getListener() });
     
      // No resets. This is the normal case. Use the snapshot
      // and any subsequent ticks. The subsequent ticks
      // are not sorted, but are played back in the order received,
      // which hopefully should be the sequence number order (i.e., no sorting necessary).
      _listener.valueUpdate(_snapshotOnHold);
     
      for (LiveDataValueUpdateBean tick : _ticksOnHold) {
        if (tick.getSequenceNumber() > snapshotSequenceNo) {
          _listener.valueUpdate(tick);
        }
      }
    } else {
      s_logger.debug("{}: Reset detected. Sending {} ticks on hold to {}",
          new Object[] {getRequestedSpecification(), _ticksOnHold.size() - resetIndex, getListener() });
     
      // This happens when the server is reset (rebooted/migrated) while subscribing.
      // We assume that the tick with sequence number = 0
      // is a full update (as LiveDataValueUpdate.getSequenceNumber() specifies).
      // Using this assumption, we first use the tick with sequence number = 0, which
      // acts as the snapshot, and then simply send any subsequent ticks in order.
      for (int i = resetIndex; i < _ticksOnHold.size(); i++) {
        LiveDataValueUpdateBean tick = _ticksOnHold.get(i);
        _listener.valueUpdate(tick);
      }
    }
     
    _ticksOnHold.clear();
    _snapshotOnHold = null;
  }
}
TOP

Related Classes of com.opengamma.livedata.client.SubscriptionHandle

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.