Talking asynchronous apps

Tutorial: Atmosphere 1.0, WebSocket Portability on the JVM - Part 2

    

The Client Side – atmosphere.js

That’s it for the server side. Now let’s use the atmosphere.js to write the client side. First, let’s look at the code (Listing 4).

Listing 4: Atmosphere.js client code

 

  $(function () {
      "use strict";
  
      var header = $('#header');
      var content = $('#content');
      var input = $('#input');
      var status = $('#status');
      var myName = false;
      var author = null;
     var logged = false;
     var socket = $.atmosphere;
     var subSocket;
     var transport = 'websocket';
 
     // We are now ready to cut the request
     var request = { url: document.location.toString() + 'chat',
         contentType : "application/json",
         trackMessageSize: true,
         shared : true,
         transport : transport ,
         fallbackTransport: 'long-polling'};
 
     request.onOpen = function(response) {
         content.html($('<p>', { text: 'Atmosphere connected using ' + response.transport }));
         input.removeAttr('disabled').focus();
         status.text('Choose name:');
         transport = response.transport;
         
         if (response.transport == "local") {
             subSocket.pushLocal("Name?");
         }   
     };  
     
     request.onTransportFailure = function(errorMsg, request) {
         jQuery.atmosphere.info(errorMsg);
         if (window.EventSource) {
             request.fallbackTransport = "sse";
             transport = "see";
         }   
         header.html($('<h3>', { text: 'Atmosphere Chat. Default transport is WebSocket, fallback is ' + request.fallbackTransport }));
     };  
     
     request.onMessage = function (response) {
     
         // We need to be logged first.
         if (!myName) return;
         
         var message = response.responseBody;
         try {
            var json = jQuery.parseJSON(message);
         } catch (e) {
             console.log('This doesn\'t look like a valid JSON: ', message.data);
             return;
         }   
        
         if (!logged) {
             logged = true;
            status.text(myName + ': ').css('color', 'blue');
             input.removeAttr('disabled').focus();
            subSocket.pushLocal(myName);
         } else {
             input.removeAttr('disabled');
             
             var me = json.author == author;
             var date = typeof(json.time) == 'string' ? parseInt(json.time) : json.time;
             addMessage(json.author, json.message, me ? 'blue' : 'black', new Date(date));
         }   
     };  
     
     request.onClose = function(response) {
        logged = false;
     }   
     
     subSocket = socket.subscribe(request);
     
     input.keydown(function(e) {
         if (e.keyCode === 13) {
             var msg = $(this).val();
            if (author == null) {
                 author = msg;
             }
                 
             subSocket.push(jQuery.stringifyJSON({ author: author, message: msg }));
             $(this).val('');
             
             input.attr('disabled', 'disabled');
             if (myName === false) {
                 myName = msg;
             }
         }       
     });     
          
     function addMessage(author, message, color, datetime) {
         content.append('<p><span style="color:' + color + '">' + author + '</span> @ ' +
             + (datetime.getHours() < 10 ? '0' + datetime.getHours() : datetime.getHours()) + ':'
             + (datetime.getMinutes() < 10 ? '0' + datetime.getMinutes() : datetime.getMinutes())
             + ': ' + message + '</p>');
     }       
  });  

 

There is a lot of extra in the code in Listing 4, so let’s only describe the atmosphere.js important parts. First, we initialize a connection (called socket in the code):

 

 var socket = $.atmosphere;

 

The next step is to define some functions callback. For this article, let’s define only a one subset. First, we define an onOpen function that gets invoked when the underlying transport is connected to the server (line 24). There we just display the transport that was used for connecting to the server. The transport is specified on the request object, which is defined as:

 

 var request = { url: document.location.toString() + 'chat',
          contentType : "application/json",
          transport : transport ,
          fallbackTransport: 'long-polling'};

 

Here we want to use the WebSocket transport by default, and fallback to long-polling in case WebSocket is not supported either by the browser or the server. In our onOpen function we just displayed which transport was used.

Note: You can also change the transport when WebSocket is failing by adding an onTransportFailure function:

request.onTransportFailure = function(errorMsg, request) {
        if (window.EventSource) {
            request.fallbackTransport = "sse";
            transport = "see";
        }   

Here for demonstration purposes, we shall look for the EventSource object (HTML5 Server Sides Events) and, if available, switch the transport to use it. The beauty here is: you don’t need to use a special API. All transports are handled the same way using the atmosphere.js.

Next we define the onMessage function, which will be invoked every time we receive data from the server

 

   request.onMessage = function (response) {
      …..
   }

 

Here we just display the received message. To connect and send data to the server, all we need to do is to invoke:

 

subSocket = socket.subscribe(request);

 

Once subscribed, we are ready to receive and send data. To send data, we are using the subSocket object returned from the subscribe operation. If the WebSocket transport is in use, the subSocket will reference the WebSocket connection (because the protocol is bi-directional), where for all other transport, a new connection will be opened every time the push operation is called:

 

subSocket.push(jQuery.stringifyJSON({ author: author, message: msg }));

 

Next, let’s add support for a really nice Atmosphere feature, which is the ability to share a connection amongst open windows/tabs. All you need to do in Atmosphere is to set the shared variable to “true” when doing a request:

 

var request = { url: document.location.toString() + 'chat',
          contentType : "application/json",
          transport : transport ,
          shared : true,
          fallbackTransport: 'long-polling'};

 

Now every time a new window or tab gets opened and the same page opened, the connection will be shared. To get notified when the “master” tabs/windows (the open that opened first), just implement

 

    request.onLocalMessage = function(message) {
      ….
    }

 

Tabs/Windows can also communicate directly by using the following function.

 

 subSocket.pushLocal(…)

 

     

Pages

Jeanfrancois Arcand
Jeanfrancois Arcand

What do you think?

JAX Magazine - 2014 - 03 Exclucively for iPad users JAX Magazine on Android

Comments

Latest opinions