Overview

Elektron WebSocket API enables easy integration into a multitude of client technology environments such as scripting and web. This API runs directly on your TREP infrastructure or the Thomson Reuters platform and presents data in an open (JSON) readable format. The API supports all Thomson Reuters Elektron data models and can be integrated into multiple client technology standards e.g. JavaScript, Python, R, .Net etc.

The web browsers JavaScript runtime is a single-threaded environment by default. However, the HTML standard lets developers implement multi threads JavaScript application in web browser by introducing the Web Workers feature that lets web browsers run JavaScripts in a main thread and a background thread (workers thread).

This article shows how to implement the Elektron WebSocket API with JavaScript web application with Web Workers. It allows the Web Workers thread to handle the connection logic with ADS WebSocket server while the main thread handles the UI interaction events and displaying data.

Image icon WebWorkers1.PNG

Figure-1: Web Workers and WebSocket connection diagram

*Note: The initial release of this API is for deployed TREP customers only (i.e. to use it you will need an installed version of TREP 3.2).

WebSocket Overview

WebSocket specification defines an API that enables web pages to use the WebSockets protocol for two-way communication with a remote host. It introduces the WebSocket interface and defines a full-duplex communication channel that operates through a single socket over the Web. This specfication also later applied to other client technology sides like Python, Ruby, Go, Java etc.

Image icon WebSocket1.PNG

Figure-2: WebSocket connection diagram

Web Workers Overview

Web Workers allows for concurrent execution of the browser threads and one or more JavaScript threads running in the background. The main JavaScript application thread communicates with the Workers thread application via an event model and the "postMessage()" function. The postMessage() function can accept either a string or JSON object as its single argument.

Image icon WebWorkers2.PNG

Figure-3: Web Workers communication diagram

Please see an example code below.

Main.js:

let wk = new Worker("Workers.js"); //Create a workers object

//Receive events/messages from Web Workers Worker.js file
wk.addEventListener("message", function (oEvent) {
  console.log('Message from Workers: ', e.data);
}, false);

wk.postMessage('This is from Main.js'); //send message to Worker.js

Worker.js:

//Receive events/messages from Main.js file
self.addEventListener('message', function(e) {
  self.postMessage(e.data);
}, false);

self.postMessage('This is from Workers.js'); //send message to Main.js

 

There are two types of the Web Workers, Dedicated Workers and Shared Workers. This example covers only how to implement the Elektron WebSocket API with JavaScript web browser application with Dedicated Workers. This article covers only how to implement the Elektron WebSocket API with JavaScript web browser application with Dedicated Workers.

Supported Web Browsers

The example supports the following web browsers (based on the WebSocket and Web Workers browser supported)

  • Google Chrome
  • Mozilla Firefox
  • IE11

Application files

The web application contains the following example files and folder:

  1. index.html: The application HTML page

  2. app/market_price_app.js: The application main file

  3. app/ws_workers.js: The application Web Workers file

  4. css/cover.css: The application CSS file

  5. libs/jquery-3.2.1.min.js: jQuery library file

  6. bootstrap/css, bootstarp/fonts and bootstrap/js folders: The folders for Bootstrap CSS and libraries files

  7. node_modules folder: Folder for Node.js and Express.js modules for web server running

  8. server.js: A web server application file

  9. package.json: The Project npm dependencies file

Message Structure

The message between market_price_app.js and ws_worker.js care in JSON format. They use JSON property 'command' to tell what should they handle the incoming message.

{
  'commandObj': <JSON message for sending to ADS>,
  'command': '<command>'
}

The list of command values between market_price_app.js and ws_workers.js are following:

  • 'connect': Inform ws_workers.js to establish a WebSocket connection with ADS server

  • 'login': Inform ws_workers.js to send the Login Request message to ADS server

  • 'requestitem': Inform ws_workers.js to send the Item Request message to ADS server

  • 'closeitem': Inform ws_workers.js to send the Item Close Request message to ADS server

  • 'pong': Inform ws_workers.js to send the Pong message to ADS server

  • 'logout': Inform ws_workers.js to disconnect ADS server

  • 'incomingMsg': Inform market_price_app.js to display incoming data in web browser

Application Code Implementation Details

This article  focus on the market_price_app.js and ws_workers.js files which demostrate how to implement the Elektron WebSocket application with Web Workers.

  1. Firstly, we create JavaScript  market_price_app.js and ws_workers.js files in "app" folder.

  2. We define all initial required variables in the market_price_app.js and ws_workers.js files. All variables will be used for keeping the the applicaiton WebSocket and Web Workers information.  
    /**market_price_app.js**/
    (function ($) {
    
      //Define variables
      var serverurl = '',
        username = '',
        itemID = 0,
        wk = new Worker("./app/ws_workers.js"); //Create a workers object
      const protocol = 'tr_json2';
      const loginID = 1;
    
    })($);
    
    /**ws_workers.js**/
    (function () {
    
      //Define WebSocket and protocol variables
      let ws = null;
    
      //Define based object response to market_price_app.js 
      let onMessage_response = {
        'command': 'incomingMsg',
        'msg': null
      };
    
    })();

     

  3. Next, we create the addEventListener() functions callback in both files for receiving the messages from each other. The addEventListener() functions check the message's data and message's command properties to verify the operation.

    /**market_price_app.js**/
    (function ($) {
    
      //Define variables
      ...
      //Receive events from Web Workers ws_worker.js file
      wk.addEventListener("message", function (oEvent) {
        let response = oEvent.data;
        //Get command parameter to identify operation
        let command = response.command;
    
      }, false);
    
    })($);
    
    /**ws_workers.js**/
    (function () {
    
      //Define WebSocket variable
      ...
      //Receive message from market_price_app.js
      self.addEventListener('message', function (e) {
        let data = e.data;
        //Get command parameter to identify operation
        let command = data.command;
    
      }, false);
    
    })();

     

  4. Then we define all WebSocket required functions in ws_workers.js file. 

    /**ws_workers.js**/
    //Establish WebSocket connection success
    function onOpen(event) {
      //Inform market_price_app.js the connection request is success
    }
    
    //Receives incoming message from WebSocket
    function onMessage(event) {
      //Send incoming ADS message to market_price_app.js
    }

     

  5. Next, we implement a WebSocket connection logic. The market_price_app.js receives user input for ADS Server IP Address and WebSocket port from the index.html web page, then market_price_app.js sends the WebSocket URL to ws_wokers.js file with command 'connect' to establish a connection via postMessage() function. The ws_workers.js calls the WebSocket connect() function to establish a WebSocket connection. 

    /**market_price_app.js**/
    $('#btnConnect').click(function () {
      serverurl = 'ws://' + $('#txtServerurl').val() + '/WebSocket';
      connect(serverurl);
    });
    
    //Establish WebSocket connection
    function connect(serverurl) {
      $('#commandsPre').html('ws = new WebSocket("' + serverurl + '", "' + protocol + '");');
      let connectObj = {
        'commandObj': {
          'serverurl': serverurl,
          'protocol': protocol
          },
        'command': 'connect'
      };
      //Send message to Web Workers
      wk.postMessage(connectObj);
    }
    
    /**ws_workers.js**/
    
    //Receive message from market_price_app.js
    self.addEventListener('message', function (e) {
      let data = e.data;
      //Get command parameter to identify operation
      let command = data.command;
    
      if (command === 'connect') {
        connect(data.commandObj); //Establish WebSocket connection
      } 
    }, false);
    
    //Establish WebSocket connection and binding all events functions
    function connect(commandObj) {
      ws = new WebSocket(commandObj.serverurl, commandObj.protocol);
      ws.onopen = onOpen;
      ws.onmessage = onMessage;
      ...
    }

     

  6. Next, we implement the ws_workers.js's onOpen() function callback to handle a WebSocket connect event. When the application success connect to ADS server, this onOpen() function sends a message to notify market_price_app.js that the connection is established. The market_price_app.js will change the Web UI to inform users regarding this connection event. 

    /**ws_workers.js**/
    function onOpen(event) {
      var onOpen_response = {
        'command': 'connect',
        'msg': 'Connected'
      };
      self.postMessage(onOpen_response);
    }
    
    /**market_price_app.js**/
    wk.addEventListener("message", function (oEvent) {
    
      let response = oEvent.data;
      //Get command parameter to identify operation
      let command = response.command;
      if (command === 'connect') { //WebSocket connection event
        processConnectionEvent(response);
      } 
    
    }, false);
    
    function processConnectionEvent(response) {
      $('#btnConnect').html(response.msg);
    }

     

  7. When users click a connect button in index.html page, the market_price_app.js receives a user name for creating a login command. The market_price_app.js creates the JSON Login request message and sends it to ws_workers.js for sending this message to ADS server. 

    /**market_price_app.js**/
    $(document).ready(function () {
      //connect
      //handle login button
      $('#btnLogin').click(function () {
        let username = $('#txtUsername').val();
        sendLoginrequest(username);
      });
    }
    
    //Send a Login Request message to ADS WebSocket
    function sendLoginrequest(username) {
      //Create Login request message
      let loginMsg = {
        'ID': loginID,
        'Domain': 'Login',
        'Key': {
          'Elements': {
            'ApplicationId': '256',
            'Position': '127.0.0.1'
          },
          'Name': ''
        }
      };
      loginMsg.Key.Name = username;
      $('#commandsPre').html('Sending Login request message to Web Workers: WebWorkers.post(' + JSON.stringify(loginMsg, undefined, 2) + ');');
      //Printing the JSON Login request message in Web UI
    
      let loginObj = {
        'commandObj': loginMsg,
        'command': 'login'
      }
      //Send Login message to Web Workers
      wk.postMessage(loginObj);
    }
    
    /**ws_workers.js**/
    
    //Receive message from market_price_app.js
    self.addEventListener('message', function (e) {
      let data = e.data;
      //Get command parameter to identify operation
      let command = data.command;
    
      if (command === 'connect') {
        connect(data.commandObj); //Establish WebSocket connection
      } else {
        sendOMMmessage(data.commandObj);
      }
    }, false);
    
    //Send message to ADS WebSocket
      function sendOMMmessage(commandObj) {
        ws.send(JSON.stringify(commandObj));
    }

     

  8. Next, we implement the WebSocket onMessage() function in ws_workers.j file to receive incoming message from the ADS WebSocket server and dispatch it to market_price_app.js file. The market_price_app.js sends it to the processData() function displaying that data in Web UI. 

    /**ws_workers.js**/
    //Receives incoming message from WebSocket
    function onMessage(event) {
      let incomingdata = JSON.parse(event.data.toString());
      //Iterate each JSON message and send it to market_price_app.js
      for (let index = 0; index < incomingdata.length; index++) {
        onMessage_response.msg = incomingdata[index];
        self.postMessage(onMessage_response); //send message to market_price_app.js
      }
    }
    
    /**market_price_app.js**/
    //Receive events from Web Workers ws_worker.js file
    wk.addEventListener("message", function (oEvent) {
      let response = oEvent.data;
      //Get command parameter to identify operation
      let command = response.command;
    
      if (command === 'connect') { //WebSocket connection event
        processConnectionEvent(response);
      } else if (command === 'incomingMsg') { //Receive incoming messages from ADS WebSocket
        processData(response.msg);
      } 
    
    }, false);

     

  9. We implement the processData() function in market_price_app.js file to display incoming ADS JSON messages (Login, data, status, etc) in the web page. We start by enhance the processData() function to support the REFFRESH_RESP message for the OMM Login domain.  

    /**market_price_app.js**/
    
    //Process incoming messages from ADS WebSocket
    function processData(data) {
      let msgtype = data.Type;
    
      //Clear previous message
      $('#messagesPre').html('');
      //If incoming message is REFRESH_RESP
      if (msgtype === 'Refresh') {
        if (data.Domain === 'Login') {
            $('#messagesPre').html('Receive: Login REFRESH_RESP:<br/>'); //Login Refresh_resp
            $('#messagesPre').html($('#messagesPre').html() + JSON.stringify(data, undefined, 2)); //Display REFRESH_RESP
        } 
      } 
    }

     

  10. Now the application can establish a connection and logged in with ADS WebSocket server. The next step is requesting Market Price data to the ADS server. The market_price_app.js file receives user input item name in the RIC code format (and optionally, a service name) from the index.html page, then creates a JSON item request message and sends it to ADS WebSocket server via ws_workers.js file.

    /**market_price_app.js**/
    
    $(document).ready(function () {
      $('#btnSubscribe').click(function () {
        let servicename = $('#txtServiceName').val();
        let itemname = $('#txtItemName').val();
        sendItemrequest(servicename, itemname);
      });
    }
    
    //Send Item Request message to ADS WebSocket
    function item sendItemrequest(service, itemname) {
      //Create stream ID, must not be 1 (Login) 
      if (itemID === 0) {
        itemID = loginID + 1;
      } else {
        itemID += 1;
      }
    
      //create Market Price request message
      let itemrequestMsg = {
        'ID': itemID,
        'Key': {
          'Name': itemname,
          'Service': service
        }
      };
    
      let itemrequestObj = {
        'commandObj': itemrequestMsg,
        'command': 'requestitem'
      }
      //Send Item Request message to Web Workers
      wk.postMessage(itemrequestObj);
    }
    
    /**ws_workers.js**/
    
    //Receive message from market_price_app.js
    self.addEventListener('message', function (e) {
      let data = e.data;
      //Get command parameter to identify operation
      let command = data.command;
    
      if (command === 'connect') {
        connect(data.commandObj); //Establish WebSocket connection
      } else {
        sendOMMmessage(data.commandObj);
      }
    }, false);
    
    //Send message to ADS WebSocket
    function sendOMMmessage(commandObj) {
        ws.send(JSON.stringify(commandObj));
    }

     

  11. The ws_workers.js file receives incoming Market Price message via the WebSocket onMessage() function.The ws_workers.js file dispatches it to the market_price_app.js's processData() function, then we implement the  processData() function to support incoming REFRESH_RESP and UPDATE_RESP messages for Market Price domain message. 

    /**market_price_app.js**/
    //Process incoming messages from ADS WebSocket
    function processData(data) {
      let msgtype = data.Type;
    
      //Clear previous message
      $('#messagesPre').html('');
      //If incoming message is REFRESH_RESP
      if (msgtype === 'Refresh') {
    
        if (data.Domain === 'Login') {
          $('#messagesPre').html('Receive: Login REFRESH_RESP:<br/>'); //Login Refresh_resp
        } else {
          $('#messagesPre').html('Receive: Data REFRESH_RESP:<br/>'); //Data Refresh_resp
        }
        $('#messagesPre').html($('#messagesPre').html() + JSON.stringify(data, undefined, 2)); //Display REFRESH_RESP
      } else if (msgtype === 'Update') { //If incoming message is UPDATE_RESP
        $('#messagesPre').html('Receive: UPDATE_RESP:<br/>' + JSON.stringify(data, undefined, 2)); //Display Update_resp
      }
    }

     

  12. The last step is handling the Ping and Pong messages. The Ping and Pong messages are the handshake message in JSON format ({ "Type": "Ping" } and { "Type": "Pong" } messages) between the ADS WebSocket server and client for monitoring a connection health. The ADS server periodically sends Ping message to applications and applications must be prepared to send Pong messages as a response to any Ping message they receive. 

    /**market_price_app.js**/
    //Process incoming messages from ADS WebSocket
    function processData(data) {
      let msgtype = data.Type;
    
      //Clear previous message
      $('#messagesPre').html('');
      //If incoming message is REFRESH_RESP
      if (msgtype === 'Refresh') {
    
        //Handle Refresh message
      } else if (msgtype === 'Update') { //If incoming message is UPDATE_RESP
        //Handle Update message
      } else if (msgtype === 'Ping') { //If incoming message is PING (server ping)
        $('#messagesPre').html('Recieve Ping:</br>' + JSON.stringify(data, undefined, 2)); //Server Ping
        sendPong();
      }
    }
    
    //Send { 'Type': 'Pong' } for acknowledge Server PING
    function sendPong() {
      let pongObj = {
        'commandObj': { 'Type': 'Pong' },
        'command': 'pong'
      }
      //Send PONG message to Web Workers
      wk.postMessage(pongObj);
      $('#commandsPre').html('Sending Client Pong: ws.send(' + JSON.stringify({ 'Type': 'Pong' }, undefined, 2) + ');');
    }

     

Running the application

The application source code is available at GitHub. You can get it via the following git command 

$>git clone git@github.com:TR-API-Samples/Article.EWA.JavaScript.WebWorkersApp.git

This example application requires  the followinng libraries and runtime

  1. jQuery 3.2.1 JavaScript library (included with the project)
  2.  Bootstrap 3.3.7 css library (included with the project)
  3. Node.js runtime

please check the README.md in the source code project for more detail.

How to run this example

Firstly, get the project via the above git command, and then run npm install command in the command prompt to install all the dependencies required to run the sample in a subdirectory called node_modules/

 Image icon npm_install.png

If the machine is behind a proxy server, you need to configure Node.js uses proxy instead of a direct HTTP connection via the following command in command prompt 

set https_proxy=http://<proxy.server>:<port>

 

Run node server.js command in the command prompt to start the web server at HTTP port 8080 Image icon run_server.png

Open http://localhost:300/index.html in the web browsers (IE11, Chorme and Firefox). When you open index.html in your browser. The application page will show in the web browser.Image icon application_screen.png

Figure-6: The application page that receives user input

Then, input the ADS WebSocket IP and port and click connect to establish a connection. Once connection success, the connect button label changes to "connected".

Image icon example2.PNG

Figure-7: The WebSocket application is now established a connection with ADS

Then input user name and click a Login button to send a Login request message to ADS.

Image icon example3.png

Figure-8: The application is now logged in with the ADS server

 

Then, input the item name in RIC code format (and optionally, service name), click subscribe. The page shows outgoing request message and incoming Refresh, Update messages from ADS WebSocket server.

Image icon example4.png

Figure-7: The application is now subscribing Market Price data with ADS server

 

Finally, the application is automatic sends Pong message back to the ADS server when it receives Ping handshake message from ADS

Image icon example5.png

Figure-8: The application is now subscribing Market Price data with ADS server

References

For further details, please check out the following resources:

For any question related to this article or Elektron WebSocket API page, please use the Developer Community Q&A Forum.