XPages and Bluemix: Sending a targeted Websockets message to specific XPages

In this article I will demonstrate how the use of socket.io “rooms” enables us to send message to users who are only accessing specific pages within our application, rather than blanket messages to all users.

Introduction

In the previous article I demonstrated how to use a message POST to the node.js server which could then be turned into a chat message and sent out to all users. While this is a nice example it only serves as such and does not have significant business value. In this article I will begin the peel back the potential for much greater application flexibility through the controlled use of targeted Websockets messages to users of an application.

Most applications have more than one “page” within it and we may wish to send a message to users of one page rather than all pages. Conversely we may want to only send messages to users who are viewing certain types of information wherever they are within the application.

In this article we are still going to use the chat example but this will be the last time we use “chat” as the use case. In future articles I will look into more practical applications of Websockets messaging within an application.

Using rooms within socket.io

Looking at the socket.io documentation for rooms and namespaces you can see that the API exposes the ability to create individual communication channels between the server and the application users.

How this translates to our application on the node.js server looks like this:

  socket.on('joinRoom', function(room, name){
    socket.join(room);
    io.sockets.in(room).emit('notice', name+" has joined");
    console.log(name+" has joined room "+room)
  })

When the “joinRoom” event is registered on the server then the socket is joined to the room name which is passed in. A message is the broadcast specifically out to all the existing room members that the new user has joined. Note that this message is room specific because of the io.sockets.in(room) rather than a blanket message to all users.

With this code in place on any page we can register our application page (chat room in this case) with the socket server. In my case I created a generic function to take the name of the “room” from the XPage URL. The following client side JavaScript sends the “joinRoom” request to the server.

  nickname=$('.username').text()
  // Send message to server that user has joined WinX.nsf/xRoom1.xsp?open&login
  var xpageName = location.href.split('.nsf/')[1] //xRoom1.xsp?open&login
  xpageName = xpageName.split('.xsp')[0] //xRoom1
  socket.emit('joinRoom', xpageName, nickname);

On the server we can then see the console.log as someone joins the room:

s1
We can then have multiple users in individual “rooms” which are in this case 3 separate xPages xRoom1.xsp, xRoom2.xsp, xRoom3.xsp From the original chat room I am able to sen all three rooms a blanket message because they are all “listening” for “msg” as in the previous articles

 socket.on('msg', function(data) {
    console.log('msg here')
    console.log(data)
    redisClient.lpush('messages', JSON.stringify(data));
    redisClient.ltrim('messages', 0, 99);
    socket.broadcast.emit('msg', data);        //broadcast to all users indiscriminately
  });

s2

Sending a message to a specific room

Modifying the original POST code which was shown in the previous article I was then able to create an XPage which will send a targetted message to a specific room. To do this, all I had to do was pass in an additional POST parameter of the room I wanted to send a message to.

I create a new Master XPage which had 3 fields on it – one for each room. Each of the individual fields had a “room” attribute which allows me to pick up a value to specify which room to send it to.

<div id="msgRoom1" class="msgCenter">
	<input placeholder="Send a message to Room1" room="xRoom1" autocomplete="off" autofocus="autofocus" />
	<button type="button">Send</button>
</div>
<br/><br/>
<div id="msgRoom2" class="msgCenter">
	<input placeholder="Send a message to Room2"  room="xRoom2" autocomplete="off"  />
	<button type="button">Send</button>
</div>
<br/><br/>
<div id="msgRoom3" class="msgCenter">
	<input placeholder="Send a message to Room3"  room="xRoom3" autocomplete="off" />
	<button type="button">Send</button>
</div>

s3

In the following code we bind to each of the buttons so that when the are clicked they:

  • set msgField to be the jQuery object representing the pertinent field
  • create the data object to send to the socket server
  • create the newMessage passing in data

The new message function then:

  • POSTs the data object at the “/roomPost” path on the server
  var socketServerURL = (location.href.indexOf('copper.xomino.com')>0) ? "http://copper.xomino.com:3000" : 'http://xominosocket.mybluemix.net'
  var socket = io.connect(socketServerURL)

$('.msgCenter Button').on('click', function(){
  	var msgField = $(this).prev('INPUT')
	var data = { text: msgField.val(), nickname: nickname, when: new Date(), room: msgField.attr("room") };
	sendMessageToRoom(data)
	newMessage(data);
	// Clear the message field
	msgField.val('');
})

var sendMessageToRoom = function(data){
	console.log(data)
	$.ajax({ url:
	        socketServerURL+"/roomPost",
		type: "POST",
		data: data
	}).done(function( msg ) {
		console.log( msg);
	});
}

We can see the message come through on the server

s4

You can also see from the log that I was able to join 3 rooms from the Message Center page – this is as simple as creating three requests to join:

  //client side JavaScript
  socket.emit('joinRoom', 'xRoom1', nickname);
  socket.emit('joinRoom', 'xRoom2', nickname);
  socket.emit('joinRoom', 'xRoom3', nickname);

Site in action
The best way to see all this is to see it in action – as you can see from the video below.

Moving the application to Bluemix

Working locally I used to following code to determine if I was on copper.xomino.com or another server (demo.xomino.com or different again)

  var socketServerURL = (location.href.indexOf('copper.xomino.com')>0) ? "http://copper.xomino.com:3000" : 'http://xominosocket.mybluemix.net'
  var socket = io.connect(socketServerURL)

I had to make a slight change to my cors code in the local node server setup. I looked at the cors npm site to see how to dynamically add cors support and found the answer and modified my code accordingly. So you need to make sure you

//Marky adding CORS from copper
//app.use(cors()); //not used for whitelists

var whitelist = ['http://copper.xomino.com', 'http://demo.xomino.com', 'http://marky.psclistens.com'];
var corsOptions = {
  origin: function(origin, callback){
    var originIsWhitelisted = whitelist.indexOf(origin) !== -1;
    callback(null, originIsWhitelisted);
  }
};

marky.psclistens.com is the same local server as copper.xomino.com (I use a hosts file to make them both look at localhost). But this allows me to play with cors and in this case talk to the local or bluemix node server without needing another “Domino” server.

s6

Because the site running locally is already connected to the Jazz Hub Git repository, as with the previous examples all I have to do is commit the changes locally, push to the Jazz Hub repository, the application will then be re-built and re-deployed to Bluemix.

It’s really about as simple as that. Because the code is already primed to check to see if we are looking locally or at Bluemix, a new URL for the application now looks at Bluemix.

s5

 

So why do we need Bluemix again?

While the development was performed locally, the intention is to be able to create a capability which does not require the user to have a locally running node.js server. That is where Bluemix comes in. In essence what we are creating is a cloud hosted Websockets service which can be integrated into existing applications.

 

Conclusion

In this article we have seen that with a little creativity we are able to register different chat rooms within the same application. There are other ways to do this of course but the point of this example was to demonstrate the ability to send a specific message to specific users looking at an individual XPage.

In the next article we will take a look at the practical implications of this within an XPages application.

 

2 thoughts on “XPages and Bluemix: Sending a targeted Websockets message to specific XPages

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s