Kaazing WebSocket Gateway

Orange guy with box of questions

Creating an XMPP-Driven Application in JavaScript

Technologies used: JavaScript, XMPP, and Kaazing WebSocket Gateway

This document contains the following sections:

Introduction

There are several innovations within the HTML 5 specification that will forever change the direction of the Web. Two of these innovations in particular--Web sockets and Server-sent events-- revolutionize the way Web applications are going to be architected, developed, and deployed. Until recently, bi‑directional browser communication has been an elusive goal of the Web community. However, with updates to the HTML 5 specification, developers can now use a full-duplex communications channel that operates over a single socket.

HTML 5's WebSocket interface enables communication from the browser to any TCP-based back-end service (for example, JMS, JMX, IMAP, and Jabber). For example, it is now possible to avoid convoluted architectures by simply channeling certain protocols to the browser over HTTP and Web applications can now be deployed without the need for a traditional Web application server.

Kaazing WebSocket Gateway delivers an enterprise WebSocket server that enables full-duplex communication from the browser to any TCP-based back-end service. Kaazing WebSocket Gateway is the first enterprise solution that understands the WebSocket protocol and enables direct communication with all major enterprise protocols (for example, Stomp, JMS, IMAP, JDBC, and Jabber).

Overview of XMPP

Extensible Messaging and Presence Protocol (XMPP) is an open XML technology for presence and real-time communication, developed by the Jabber open-source community in 1999. XMPP was formalized by the Internet Engineering Task Force (IETF) between 2002 and 2004 and the protocol continues to be extended through the XMPP Standards Foundation.

XMPP consists of XML streams that enable any two entities on the Internet to exchange messages, presence, and other structured information. Chat and presence are obvious candidates for real-time communication. Examples of chat solutions that use XMPP today are Google Talk and Apple iChat. For more information about XMPP, see http://www.xmpp.org/xsf.

Unlike Stomp, XMPP messages are not sent in frames, but in XML streams that must be parsed carefully. The following list of commands are some of the standard XMPP commands, but there are many other extensions, see http://www.xmpp.org/extensions/.

  • Connect and disconnect
  • Register
  • Check roster
  • Send messages
  • Set status
  • Communicate presence

Example XMPP Communication

The following is an example of XMPP communication from the client to the server. In this case, the server can be a server or another client.

First, a stream is initialized between client and server:

<?xml version='1.0'?>
  <stream:stream
  to='example.com'
  xmlns='jabber:client'
  xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>

The server's response looks something like this:

<?xml version='1.0'?>
  <stream:stream
  from='example.com'
  id='someid'
  xmlns='jabber:client'
  xmlns:stream='http://etherx.jabber.org/streams'
  version='1.0'>

Once the stream is initialized, encryption, authentication, and resource binding takes place so the client send messages to the server. After that, the client can send a chat message like this:

<message from='rocky@example.com'
 to='sean@example.com'
 xml:lang='en'>
 <body>Hi Sean</body>
</message>

The chat message response looks like this:

<message from='sean@example.com'
 to='rocky@example.com'
 xml:lang='en'>
 <body>Hi Rocky!</body>
</message>

WebSocket and XMPP

WebSocket enables direct communication from the browser to an XMPP server. Kaazing WebSocket Gateway provides an XmppClient client library, which radically simplifies the application design and eliminates the need for using convoluted server- and client-side architectures to map server-side protocols to the browser over HTTP. Web developers can use the browser’s native JavaScript support and code directly against the back-end services without the need for custom Servlets or server-side programming.

Using the XmppClient JavaScript client library, you can take advantage of many XMPP features, making JavaScript a first-class citizen in XMPP messaging systems. The XmppClient JavaScript library uses Kaazing's WebSocket client library, because XMPP is a text-based protocol. Since the implementation is layered on top of WebSocket--and thus the entire stack of Kaazing HTML 5 Communications client libraries--applications developed using the XmppClient JavaScript library are provided with guaranteed persistence, reliability, and message-receipt acknowledgment all the way to the browser.

Overview of Openfire

Openfire is a real-time XMPP Instant Messaging and Groupchat server licensed under the Open Source GPL. A preconfigured Openfire server is part of the Kaazing WebSocket Gateway demo bundle. For more information about Openfire, see http://www.igniterealtime.org/projects/openfire/index.jsp

Creating an XMPP-Driven Application in JavaScript

Before you start, take a look at the finished chat Web application that you are going to build in this tutorial. The following image shows the chat application (in the browser) communicating with a desktop chat client (Spark):

Chat app in action

Setting Up the Environment

Before you start building the XMPP-driven application, you must download, install, and run the Kaazing WebSocket Gateway demo bundle and some additional software.

Kaazing WebSocket Gateway

Download, install, and run Kaazing WebSocket Gateway by performing the following steps. Refer to Getting Started for more information.

  1. Navigate to KAAZING_HOME/bin folder and run gateway.start or gateway.start.bat.
  2. Verify that Kaazing Gateway is working by opening a browser and navigating to http://localhost:8000/documentation/index.html (The home page should display).

Openfire XMPP Server

Start the preconfigured Openfire XMPP server, which is part of the Kaazing WebSocket Gateway demo bundle, by performing the following steps:

  1. Navigate to OPENFIRE_HOME/bin.
  2. Run openfire. The Openfire console displays as shown in the following image:

Openfire XMPP server started

Spark XMPP Client

Download and install the Spark XMPP desktop client by performing the following steps:

  1. Download the Spark XMPP client from http://www.igniterealtime.org/projects/spark/.
  2. Install Spark by running the spark executable file.
  3. Accept the default settings.
  4. After the installation completes, select Run Spark and click Finish.
  5. Log in to Openfire with one of the preconfigured accounts: Username rocky, Password welcome1, and Server localhost as shown in the following image:xmpp tutorial

  6. The Spark client connects to the Openfire XMPP server that is already running, and displays the other (preconfigured) member (sean) in the friends group as offline as shown in the following image:

Spark client, logged in

Starter Files

A starter file--chat.html--is located in the KAAZING_HOME/web/tutorials/xmpp-javascript directory of the Kaazing Gateway demo bundle. For your convenience, a completed example file--chat-completed.html--is also included.

Creating the XMPP-Driven Application

  1. Navigate to KAAZING_HOME/web/tutorials/xmpp-javascript/.
  2. Open the chat.html file in your preferred editor. The chat.html file is a simple HTML file. It contains a few TODO sections. This is where you add extra code.
  3. Make a backup copy of the file before you make any changes.
  4. First, add a reference to WebSocket.js, the JavaScript library that allows us to open a socket and fully leverage bi-directional communication for all browsers. The WebSocket.js library auto-detects any native browser support for WebSocket and Server-sent events (SSE). If the browser does not support WebSocket and SSE, the library automatically falls back to Kaazing's client-side emulation of the standard.

Replace:

<!-- TODO1 -->

With:

<script src="/html5/WebSocket.js"></script>

  1. Next, you must reference the API-specific JavaScript implementation of XMPP provided by Kaazing Gateway.

Replace:

<!-- TODO2 -->

With:

<script src="/protocol/XmppClient.js"></script>

  1. Next, add some variables..

Replace:

<!-- TODO3 -->

With:

var client;
var contactList = [];

// hard-coded reference to the WebSocket server (for simplicity)
var url = "ws://localhost:8000/jabber";

// hard-coded value of the "resource" (for simplicity)
var resource = 'spark';

// name of the remote server, derived from the roster list
var chatserver;

// Add a logging message to the window
var log = function(s) {
  var output = document.getElementById('console');
  output.innerHTML = "<div class='line'>" + s + "</div>" +   output.innerHTML;
}
// Set the status and availability of this user
var gui_setStatus = function() {
  var s = document.getElementById('status').value;
  var a = document.getElementById('availability').value.toLowerCase();
  client.setStatus(s, a);
}

// Utility method for escaping out XML characters
var gui_xescape = function(s) {
  return s.replace("&", "&amp;", "g")
  .replace("<", "&lt;", "g")
  .replace(">", "&gt;", "g")
}

  1. Next, add a roster callback function.

Replace:

<!-- TODO4 -->

With:

//Register a callback for receiving the roster, then fetch it
var gui_getRoster = function() {
  var rosterCallback = function(roster) {
    contactList = roster;
    gui_updateRoster();
  }
  client.getRoster(rosterCallback);
}

  1. Next, update the roster. The code scans the message and grabs the host name out of the sender string (for example, it extracts the host name example.com out of rocky@example.com/spark).

Replace:

<!-- TODO5 -->

With:

// update the roster list with incoming contacts
var gui_updateRoster = function() {
  var r = document.getElementById("roster");
  var s = "<h4 class=line>Contact List</h4>";
  var entity;
  
  for (var i=0; i<contactList.length; i++) {
    entity = contactList[i];
    var shortName = entity.jid.split('@')[0];
    if (chatserver == null) {
      chatserver = entity.jid.split('@')[1].split('/')[0];
    }
  
    s += "<div class=entity>" + shortName
      + (entity.status ? "<div   class=status>"
      + entity.status +"</div>": "")
      + (entity.show ? "<div   class=show>"
      + entity.show +"</div>" : "") + "</div>";
    }
    r.innerHTML = s;
}

  1. Next, add a function to format the message text. In this case, the code scans the message and grabs the sender's name out of the sender string (for example, it extracts the user name rocky out of rocky@kaazing.com/spark) and then formats the output string using a class defined in the associated stylesheet to give the sender and buddy different colors in the message console.

Replace:

<!-- TODO6 -->

With:

// format an incoming message for display in the UI
// c is the sender class
var gui_format_msg = function(sender, body, c) {
  sender = sender.split('@')[0];
    return "<b class=" + c + ">" + sender + ": </b>"
      + gui_xescape(body);
}

  1. Next, add a send function to send messages to contacts.

Replace:

<!-- TODO7 -->

With:

// Send a message to a chat user
var gui_send = function() {
  var j = document.getElementById('recipient').value;
  
  // restore the full name of the recipient
  j += '@' + chatserver + '/' + resource;
  
  // send the message in the body element
  var b = document.getElementById('body').value;
  client.sendMessage(j, b);
  
  var u = document.getElementById('username').value;
  log(gui_format_msg(u, b, "me"));
}

  1. Next, add an authentication function that challenges the user to log in.

Replace:

<!-- TODO8 -->

With:

// Send in the authentication credentials
var gui_auth = function() {
  var u = document.getElementById('username').value;
  var p = document.getElementById('password').value;
  credentials = {};
  credentials.password = p;
  
  // add localhost to the username to get the server
  // identification correct
  credentials.username = u + '@localhost';
  
  client.authenticate(credentials);
}

  1. Next, add additional functions for each of the server methods.

Replace:

<!-- TODO9 -->

With:

var gui_connect = function() {
client = new XmppClient();

// Once connected, start authentication
client.onopen = function() {
  log("<span style='font-style:italic'>Connected to server.</span>");
  gui_auth();
}

// Once authenticated, bind to the correct resource,
// get the roster of buddies, and set my status
client.onauthenticated = function() {
  client.bind(resource);
  gui_getRoster();
  gui_setStatus();
}

client.onclose = function() {
  log("<span style='font-style:italic'>Disconnected from server.</span>");
}

// Got a message from the server... display it!
client.onmessage = function(msg) {
  log(gui_format_msg(msg.from, msg.body, "them"));
}

client.onerror = function(e) {
  log("<span style='color:red'>" + e + "</span>");
}

client.onpresence = function(p) {
  var rosterName = p.from.slice(0, p.from.indexOf("/"));
  for (var i=0; i<contactList.length; i++) {
    if (contactList[i].jid === rosterName) {
      contactList[i].status = p.status;
      contactList[i].show = p.show;
    }
    gui_updateRoster();
  }
}

  1. Finally add a call to the connect function.

Replace:

<!-- TODO10 -->

With:

// Now, connect to the chat server
client.connect(url, "localhost")

  1. Save the file chat.html.

Congratulations, you just built your first JavaScript XMPP application!

Running the JavaScript XMPP Client

To test your new XMPP application, perform the following steps:

  1. Open a browser and navigate to http://[localhost:port]/tutorials/xmpp-javascript/chat.html. For example, http://localhost:8000/tutorials/xmpp-javascript/chat.html.
  2. Log in as sean (all lower case) with password welcome1.
  3. Send a message to rocky (the user that is logged in with the Spark desktop XMPP client).
  4. Verify the chat client is working by sending messages back and forth between the Web interface (chat.html) and the Spark desktop XMPP client as shown earlier.