Package com.opengamma.livedata.client

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

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

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.commons.lang.ObjectUtils;
import org.fudgemsg.FudgeContext;
import org.fudgemsg.FudgeMsg;
import org.fudgemsg.FudgeMsgEnvelope;
import org.fudgemsg.mapping.FudgeDeserializer;
import org.fudgemsg.mapping.FudgeSerializer;
import org.fudgemsg.wire.FudgeMsgReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.Lifecycle;

import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.id.ExternalId;
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.cogda.msg.CogdaLiveDataSnapshotRequestBuilder;
import com.opengamma.livedata.cogda.msg.CogdaLiveDataSnapshotRequestMessage;
import com.opengamma.livedata.cogda.msg.CogdaLiveDataSnapshotResponseBuilder;
import com.opengamma.livedata.cogda.msg.CogdaLiveDataSnapshotResponseMessage;
import com.opengamma.livedata.cogda.msg.CogdaLiveDataSubscriptionRequestBuilder;
import com.opengamma.livedata.cogda.msg.CogdaLiveDataSubscriptionRequestMessage;
import com.opengamma.livedata.cogda.msg.CogdaLiveDataSubscriptionResponseBuilder;
import com.opengamma.livedata.cogda.msg.CogdaLiveDataSubscriptionResponseMessage;
import com.opengamma.livedata.cogda.msg.CogdaLiveDataUnsubscribeBuilder;
import com.opengamma.livedata.cogda.msg.CogdaLiveDataUnsubscribeMessage;
import com.opengamma.livedata.cogda.msg.CogdaLiveDataUpdateBuilder;
import com.opengamma.livedata.cogda.msg.CogdaLiveDataUpdateMessage;
import com.opengamma.livedata.cogda.msg.CogdaMessageType;
import com.opengamma.livedata.cogda.msg.ConnectionRequestBuilder;
import com.opengamma.livedata.cogda.msg.ConnectionRequestMessage;
import com.opengamma.livedata.cogda.msg.ConnectionResponseBuilder;
import com.opengamma.livedata.cogda.msg.ConnectionResponseMessage;
import com.opengamma.livedata.cogda.server.CogdaLiveDataServer;
import com.opengamma.livedata.msg.LiveDataSubscriptionResponse;
import com.opengamma.livedata.msg.LiveDataSubscriptionResult;
import com.opengamma.transport.ByteArrayFudgeMessageSender;
import com.opengamma.transport.FudgeMessageReceiver;
import com.opengamma.transport.FudgeMessageSender;
import com.opengamma.transport.InputStreamFudgeMessageDispatcher;
import com.opengamma.transport.OutputStreamByteArrayMessageSender;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.fudgemsg.OpenGammaFudgeContext;

/**
* Live data client connecting to a COGDA server.
* <p>
* This connects to an instance of {@link CogdaLiveDataServer}.
*/
public class CogdaLiveDataClient extends AbstractLiveDataClient implements Lifecycle, FudgeMessageReceiver {

  /** Logger. */
  private static final Logger s_logger = LoggerFactory.getLogger(CogdaLiveDataClient.class);

  // Injected parameters:
  /**
   * The server name to connect to.
   */
  private String _serverName = "127.0.0.1";
  /**
   * The server port to connect to.
   */
  private int _serverPort = CogdaLiveDataServer.DEFAULT_LISTEN_PORT;
  /**
   * The Fudge context.
   */
  private FudgeContext _fudgeContext = OpenGammaFudgeContext.getInstance();
  /**
   * The user.
   */
  private final UserPrincipal _user;

  // Runtime state:
  /**
   * Holds the actual socket to the server.
   */
  private Socket _socket;
  /**
   * The message sender.
   */
  private FudgeMessageSender _messageSender;
  /**
   * The socket thread.
   */
  @SuppressWarnings("unused")
  private Thread _socketReadThread;
  /**
   * The generator of correlation identifiers.
   */
  private final AtomicLong _nextRequestId = new AtomicLong(1L);
  /**
   * The active subscription requests.
   */
  private final Map<Long, SubscriptionHandle> _activeSubscriptionRequests = new ConcurrentHashMap<Long, SubscriptionHandle>();

  /**
   * Creates an instance.
   *
   * @param user  the user to connect with, not null
   */
  public CogdaLiveDataClient(UserPrincipal user) {
    ArgumentChecker.notNull(user, "user");
    _user = user;
  }

  //-------------------------------------------------------------------------
  /**
   * Gets the server name.
   *
   * @return the server name, not null
   */
  public String getServerName() {
    return _serverName;
  }

  /**
   * Sets the server name.
   *
   * @param serverName  the server name, not null
   */
  public void setServerName(String serverName) {
    _serverName = serverName;
  }

  /**
   * Gets the server port.
   *
   * @return the server port
   */
  public int getServerPort() {
    return _serverPort;
  }

  /**
   * Sets the server port.
   *
   * @param serverPort  the server port
   */
  public void setServerPort(int serverPort) {
    _serverPort = serverPort;
  }

  /**
   * Gets the fudge context.
   *
   * @return the fudge context, not null
   */
  @Override
  public FudgeContext getFudgeContext() {
    return _fudgeContext;
  }

  /**
   * Sets the fudge context.
   *
   * @param fudgeContext  the fudge context, not null
   */
  @Override
  public void setFudgeContext(FudgeContext fudgeContext) {
    _fudgeContext = fudgeContext;
  }

  //-------------------------------------------------------------------------
  /**
   * Checks whether the specified user matches the user this client is for.
   *
   * @param user  the user to check, not null
   * @throws IllegalArgumentException if the user is invalid
   */
  protected void checkUserMatches(UserPrincipal user) {
    if (!ObjectUtils.equals(user, _user)) {
      throw new IllegalArgumentException("Specified user " + user + " does not match client user " + _user);
    }
  }

  //-------------------------------------------------------------------------
  @Override
  public boolean isEntitled(UserPrincipal user, LiveDataSpecification requestedSpecification) {
    // TODO kirk 2012-08-23 -- Implement this properly.
    return true;
  }

  @Override
  public Map<LiveDataSpecification, Boolean> isEntitled(UserPrincipal user, Collection<LiveDataSpecification> requestedSpecifications) {
    Map<LiveDataSpecification, Boolean> result = new HashMap<LiveDataSpecification, Boolean>();
    for (LiveDataSpecification ldc : requestedSpecifications) {
      result.put(ldc, isEntitled(user, ldc));
    }
    return result;
  }

  @Override
  protected void handleSubscriptionRequest(Collection<SubscriptionHandle> subHandle) {
    // TODO kirk 2012-08-15 -- Batch these up. This is just for testing.
    for (SubscriptionHandle handle : subHandle) {
      long correlationId = _nextRequestId.getAndIncrement();
      switch (handle.getSubscriptionType()) {
        case NON_PERSISTENT:
        case PERSISTENT:
          CogdaLiveDataSubscriptionRequestMessage subRequest = new CogdaLiveDataSubscriptionRequestMessage();
          subRequest.setCorrelationId(correlationId);
          subRequest.setNormalizationScheme(handle.getRequestedSpecification().getNormalizationRuleSetId());
          // REVIEW kirk 2012-08-15 -- The next line is SOOOOO UGLLYYYYY!!!!!
          subRequest.setSubscriptionId(handle.getRequestedSpecification().getIdentifiers().getExternalIds().iterator().next());
          _activeSubscriptionRequests.put(correlationId, handle);
          _messageSender.send(CogdaLiveDataSubscriptionRequestBuilder.buildMessageStatic(new FudgeSerializer(getFudgeContext()), subRequest));
          // Same thing in Cogda.
          break;
        case SNAPSHOT:
          CogdaLiveDataSnapshotRequestMessage snapshotRequest = new CogdaLiveDataSnapshotRequestMessage();
          snapshotRequest.setCorrelationId(correlationId);
          snapshotRequest.setNormalizationScheme(handle.getRequestedSpecification().getNormalizationRuleSetId());
          // REVIEW kirk 2012-08-15 -- The next line is SOOOOO UGLLYYYYY!!!!!
          snapshotRequest.setSubscriptionId(handle.getRequestedSpecification().getIdentifiers().getExternalIds().iterator().next());
          _activeSubscriptionRequests.put(correlationId, handle);
          _messageSender.send(CogdaLiveDataSnapshotRequestBuilder.buildMessageStatic(new FudgeSerializer(getFudgeContext()), snapshotRequest));
          break;
      }
    }
  }

  @Override
  protected void cancelPublication(LiveDataSpecification fullyQualifiedSpecification) {
    CogdaLiveDataUnsubscribeMessage message = new CogdaLiveDataUnsubscribeMessage();
    message.setCorrelationId(_nextRequestId.getAndIncrement());
    message.setNormalizationScheme(fullyQualifiedSpecification.getNormalizationRuleSetId());
    message.setSubscriptionId(fullyQualifiedSpecification.getIdentifiers().iterator().next());
    _messageSender.send(CogdaLiveDataUnsubscribeBuilder.buildMessageStatic(new FudgeSerializer(getFudgeContext()), message));
  }

  @Override
  public void messageReceived(FudgeContext fudgeContext, FudgeMsgEnvelope msgEnvelope) {
    s_logger.info("Got message {}", msgEnvelope);
    FudgeMsg msg = msgEnvelope.getMessage();
    CogdaMessageType msgType = CogdaMessageType.getFromMessage(msg);
    switch (msgType) {
      case SUBSCRIPTION_RESPONSE:
      case SNAPSHOT_RESPONSE:
        dispatchCommandResponse(msgType, msg);
        break;
      case LIVE_DATA_UPDATE:
        dispatchLiveDataUpdate(msg);
        break;
      default:
        s_logger.warn("Received message that wasn't understood: {}", msg);
    }
  }

  /**
   * Dispatches a message to the server.
   *
   * @param msg  the message, not null
   */
  private void dispatchLiveDataUpdate(FudgeMsg msg) {
    CogdaLiveDataUpdateMessage updateMessage = CogdaLiveDataUpdateBuilder.buildObjectStatic(new FudgeDeserializer(getFudgeContext()), msg);
    LiveDataSpecification ldspec = new LiveDataSpecification(updateMessage.getNormalizationScheme(), updateMessage.getSubscriptionId());
    LiveDataValueUpdateBean valueUpdateBean = new LiveDataValueUpdateBean(0L, ldspec, updateMessage.getValues());
    super.valueUpdate(valueUpdateBean);
  }

  /**
   * Dispatches a command response.
   *
   * @param msgType  the type, not null
   * @param msg  the message, not null
   */
  private void dispatchCommandResponse(CogdaMessageType msgType, FudgeMsg msg) {
    if (!msg.hasField("correlationId")) {
      s_logger.warn("Received subscription response message without correlationId: {}", msg);
      return;
    }
    long correlationId = msg.getLong("correlationId");
   
    SubscriptionHandle subHandle = _activeSubscriptionRequests.remove(correlationId);
    if (subHandle == null) {
      s_logger.warn("Got subscription result on correlationId {} without active subscription: {}", correlationId, msg);
      return;
    }
   
    switch (msgType) {
      case SUBSCRIPTION_RESPONSE:
        dispatchSubscriptionResponse(msg, subHandle);
        break;
      case SNAPSHOT_RESPONSE:
        dispatchSnapshotResponse(msg, subHandle);
        break;
      default:
        s_logger.warn("Got unexpected msg type {} as a command response - {}", msgType, msg);
        break;
    }
  }

  /**
   * Dispatches the response to a snapshot.
   *
   * @param msg  the message, not null
   * @param subHandle  the subscription handle, not null
   */
  private void dispatchSnapshotResponse(FudgeMsg msg, SubscriptionHandle subHandle) {
    CogdaLiveDataSnapshotResponseMessage responseMessage = CogdaLiveDataSnapshotResponseBuilder.buildObjectStatic(new FudgeDeserializer(getFudgeContext()), msg);
    LiveDataSpecification ldSpec = new LiveDataSpecification(responseMessage.getNormalizationScheme(), responseMessage.getSubscriptionId());
   
    LiveDataSubscriptionResult ldsResult = responseMessage.getGenericResult().toLiveDataSubscriptionResult();
    LiveDataSubscriptionResponse ldsResponse = new LiveDataSubscriptionResponse(subHandle.getRequestedSpecification(), ldsResult);
    ldsResponse.setFullyQualifiedSpecification(ldSpec);
    ldsResponse.setUserMessage(responseMessage.getUserMessage());
   
    LiveDataValueUpdateBean valueUpdateBean = new LiveDataValueUpdateBean(0L, subHandle.getRequestedSpecification(), responseMessage.getValues());
    ldsResponse.setSnapshot(valueUpdateBean);
    subHandle.subscriptionResultReceived(ldsResponse);
  }

  /**
   * Dispatches the response to subscription.
   *
   * @param msg  the message, not null
   * @param subHandle  the subscription handle, not null
   */
  private void dispatchSubscriptionResponse(FudgeMsg msg, SubscriptionHandle subHandle) {
    CogdaLiveDataSubscriptionResponseMessage responseMessage = CogdaLiveDataSubscriptionResponseBuilder.buildObjectStatic(new FudgeDeserializer(getFudgeContext()), msg);
    LiveDataSpecification ldSpec = new LiveDataSpecification(responseMessage.getNormalizationScheme(), responseMessage.getSubscriptionId());
   
    LiveDataSubscriptionResult ldsResult = responseMessage.getGenericResult().toLiveDataSubscriptionResult();
    LiveDataSubscriptionResponse ldsResponse = new LiveDataSubscriptionResponse(subHandle.getRequestedSpecification(), ldsResult);
    ldsResponse.setFullyQualifiedSpecification(ldSpec);
    ldsResponse.setUserMessage(responseMessage.getUserMessage());
   
    LiveDataValueUpdateBean valueUpdateBean = new LiveDataValueUpdateBean(0L, subHandle.getRequestedSpecification(), responseMessage.getSnapshot());
    ldsResponse.setSnapshot(valueUpdateBean);
   
    switch (responseMessage.getGenericResult()) {
      case SUCCESSFUL:
        super.subscriptionRequestSatisfied(subHandle, ldsResponse);
        super.subscriptionStartingToReceiveTicks(subHandle, ldsResponse);
        break;
      default:
        super.subscriptionRequestFailed(subHandle, ldsResponse);
    }
    subHandle.subscriptionResultReceived(ldsResponse);
    subHandle.getListener().valueUpdate(valueUpdateBean);
  }

  //-------------------------------------------------------------------------
  @Override
  public void start() {
    if (_socket != null) {
      throw new IllegalStateException("Socket is currently established.");
    }
    InetAddress serverAddress = null;
    try {
      serverAddress = InetAddress.getByName(getServerName());
    } catch (UnknownHostException ex) {
      s_logger.error("Illegal host name: " + getServerName(), ex);
      throw new IllegalArgumentException("Cannot identify host " + getServerName());
    }
    try {
      Socket socket = new Socket(serverAddress, getServerPort());
      InputStream is = socket.getInputStream();
      OutputStream os = socket.getOutputStream();
      _messageSender = new ByteArrayFudgeMessageSender(new OutputStreamByteArrayMessageSender(os));
     
      login(is);
     
      InputStreamFudgeMessageDispatcher messageDispatcher = new InputStreamFudgeMessageDispatcher(is, this);
      Thread t = new Thread(messageDispatcher, "CogdaLiveDataClient Dispatch Thread");
      t.setDaemon(true);
      t.start();
      _socketReadThread = t;
     
      _socket = socket;
    } catch (IOException ioe) {
      s_logger.error("Unable to establish connection to" + getServerName() + ":" + getServerPort(), ioe);
      throw new OpenGammaRuntimeException("Unable to establish connection to" + getServerName() + ":" + getServerPort());
    }
   
  }

  protected void login(InputStream is) throws IOException {
    ConnectionRequestMessage requestMessage = new ConnectionRequestMessage();
    requestMessage.setUserName(_user.getUserName());
    _messageSender.send(ConnectionRequestBuilder.buildMessageStatic(new FudgeSerializer(getFudgeContext()), requestMessage));
    // TODO kirk 2012-08-22 -- This needs a timeout.
    FudgeMsgReader reader = getFudgeContext().createMessageReader(is);
    FudgeMsg msg = reader.nextMessage();
    ConnectionResponseMessage response = ConnectionResponseBuilder.buildObjectStatic(new FudgeDeserializer(getFudgeContext()), msg);
    switch(response.getResult()) {
      case NEW_CONNECTION_SUCCESS:
      case EXISTING_CONNECTION_RESTART:
        // We're good to go!
        // TODO kirk 2012-08-15 -- Add logic eventually for connection restart semantics.
        s_logger.warn("Successfully logged into server.");
        break;
      case NOT_AUTHORIZED:
        // REVIEW kirk 2012-08-15 -- Is this the right error?
        throw new OpenGammaRuntimeException("Server says NOT_AUTHORIZED");
    }
  }

  @Override
  public void stop() {
  }

  @Override
  public boolean isRunning() {
    return ((_socket != null) && (_socket.isConnected()));
  }

  //-------------------------------------------------------------------------
  /**
   * A simple test that runs against localhost. Only useful for protocol development.
   *
   * @param args Command-line args. Ignored.
   * @throws InterruptedException Required to make the compiler happy
   */
  public static void main(final String[] args) throws InterruptedException { // CSIGNORE
    CogdaLiveDataClient client = new CogdaLiveDataClient(UserPrincipal.getLocalUser());
    //client.setServerName("cogdasvr-lx-1.hq.opengamma.com");
    client.start();
   
    LiveDataSpecification lds = new LiveDataSpecification("OpenGamma", ExternalId.of("SURF", "FV2DBEURUSD12M"));
    LiveDataSubscriptionResponse response = client.snapshot(UserPrincipal.getLocalUser(), lds, 60000L);
    s_logger.warn("Snapshot {}", response);
    List<LiveDataSpecification> subs = new LinkedList<LiveDataSpecification>();
    subs.add(lds);
    subs.add(new LiveDataSpecification("OpenGamma", ExternalId.of("SURF", "ASIRSEUR49Y30A03L")));
    subs.add(new LiveDataSpecification("OpenGamma", ExternalId.of("SURF", "FV1DRUSDBRL06M")));
    subs.add(new LiveDataSpecification("OpenGamma", ExternalId.of("ICAP", "SAUD_9Y")));
    subs.add(new LiveDataSpecification("OpenGamma", ExternalId.of("ICAP", "GBP_5Y")));
    subs.add(new LiveDataSpecification("OpenGamma", ExternalId.of("ICAP", "GBPUSD7M")));
    LiveDataListener ldl = new LiveDataListener() {
      @Override
      public void subscriptionResultReceived(LiveDataSubscriptionResponse subscriptionResult) {
        s_logger.warn("Sub result {}", subscriptionResult);
      }

      @Override
      public void subscriptionResultsReceived(final Collection<LiveDataSubscriptionResponse> subscriptionResults) {
        s_logger.warn("Sub result {}", subscriptionResults);
      }

      @Override
      public void subscriptionStopped(LiveDataSpecification fullyQualifiedSpecification) {
        s_logger.warn("Sub stopped {}", fullyQualifiedSpecification);
      }

      @Override
      public void valueUpdate(LiveDataValueUpdate valueUpdate) {
        s_logger.warn("Data received {}", valueUpdate);
      }
     
    };
    client.subscribe(UserPrincipal.getLocalUser(), subs, ldl);
   
    client.subscribe(UserPrincipal.getLocalUser(), new LiveDataSpecification("OpenGamma", ExternalId.of("SURF", "NO_SUCH_THING")), ldl);
   
    Thread.sleep(100000000L);
  }

}
TOP

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

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.