/// <summary> /// Sends the execution report back to the order source. /// </summary> /// <param name="tenant">The identifier of the originator of the order.</param> /// <param name="message">The execution report.</param> public void SendReport(String tenant, Teraque.Message message) { // Add this execution report to the queue of messages that this broker must return to the order source. Note that the thread used to send the message // will go to sleep if there are no more messages to be sent, so we need to wake it up if it's waiting. lock (this.syncRoot) { SourceContext sourceContext = this.sourceContextMap[tenant]; Queue <Teraque.Message> messageQueue = sourceContext.MessageQueue; messageQueue.Enqueue(message); if (messageQueue.Count == 1) { sourceContext.OrderEvents.Set(); } } }
/// <summary> /// Initializes a new instance of the BrokerContext class. /// </summary> public BrokerContext(BrokerInfo brokerInfo) { // Initialize the object. this.nameField = brokerInfo.Name; this.symbolField = brokerInfo.Symbol; // This document contains the configuration of the connections used to route orders back to the source of the order. XDocument xDocument = XDocument.Load(Environment.ExpandEnvironmentVariables(Settings.Default.SourceConfigurationFile)); // This will load in the properties of each of sources into a dictionary that is used to manage the stream of messages back to the source that // originated the order. foreach (XElement xElement in xDocument.Root.Elements("Source")) { // Don't bother to connect to the source if they aren't active. Boolean isActive = Convert.ToBoolean(xElement.Attribute("IsActive").Value); if (isActive) { // This provides an operating context for the connection back to the source of the order. SourceContext sourceContext = new SourceContext(); // This is the name of the source. String tenant = xElement.Attribute("Name").Value; // This properties are used to connect us to a web service that will deliver the messages back to the source. ClientInfo clientInfo = sourceContext.ClientInfo; clientInfo.EndpointName = xElement.Attribute("Endpoint").Value; clientInfo.Password = xElement.Attribute("Password").Value; clientInfo.UserName = xElement.Attribute("UserName").Value; // This thread will asynchronously pull messages out of the message queue and deliver them to the source. Thread thread = new Thread(this.SourceThread); thread.Start(sourceContext); thread.Name = String.Format("{0} connection to {1}", brokerInfo.Symbol, tenant); sourceContext.Thread = thread; // This acts as a routing map to deliver messages back to the source of the orders based on the name of the source. this.sourceContextMap.Add(tenant, sourceContext); } } }
/// <summary> /// Thread used to communicate with the source of the orders. /// </summary> /// <param name="state">The generic thread parameter.</param> public void SourceThread(Object state) { // This data structure provides the context for this thread. It contains all the information needed to manage a connection to a tenant that has // generated orders and is now waiting for execution reports. SourceContext sourceContext = state as SourceContext; // Construct a new web service for the source of the order so we can report our executions. ClientInfo clientInfo = sourceContext.ClientInfo; WebServiceClient webServiceClient = new WebServiceClient(clientInfo.EndpointName); webServiceClient.ClientCredentials.UserName.UserName = clientInfo.UserName; webServiceClient.ClientCredentials.UserName.Password = clientInfo.Password; // This thread will continue to pull messages out of the queue while (true) { // The thread will wait here until there is an order to process. sourceContext.OrderEvents.WaitOne(); List <Teraque.Message> messages = new List <Teraque.Message>(); lock (this.syncRoot) { Queue <Teraque.Message> messageQueue = sourceContext.MessageQueue; while (messageQueue.Count != 0 && messages.Count < BrokerContext.chunkSize) { messages.Add(messageQueue.Dequeue()); } if (messageQueue.Count == 0) { sourceContext.OrderEvents.Reset(); } } // This will guarantee that the execution report is returned to the order originator. If it can't send the message, it will try to reconstruct the // web service and try again. while (true) { try { // If the web service has been faulted (or timed out), then reopen it. Note that we provide a healthy sleep time so that we don't // constantly spin here when the simulator isn't available. if (webServiceClient.State != CommunicationState.Opened) { webServiceClient = new WebServiceClient(clientInfo.EndpointName); webServiceClient.ClientCredentials.UserName.UserName = clientInfo.UserName; webServiceClient.ClientCredentials.UserName.Password = clientInfo.Password; } // If the message sent to the web service is successful, then we'll break out of the infinite loop. Otherwise, we'll keep on trying to send // this message until we're terminated externally. webServiceClient.ReportExecution(messages.ToArray()); break; } catch { // If the web service has been faulted (or timed out), then reopen it. Note that we provide a healthy sleep time so that we don't // constantly spin here when the simulator isn't available. if (webServiceClient.State != CommunicationState.Opened) { webServiceClient = new WebServiceClient(clientInfo.EndpointName); webServiceClient.ClientCredentials.UserName.UserName = clientInfo.UserName; webServiceClient.ClientCredentials.UserName.Password = clientInfo.Password; Thread.Sleep(BrokerContext.retryTime); } } } } }