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.

 

Multiple Chrome Incognito browsers share the same data

In a previous post I talked about how going porn-mode on your browser is great for a developer. What I didn’t realize until today is that incognito mode is not as unique as I want it to be.

We use localStorage in one of the applications we are developing and I had issues when I was opening up multiple Chrome Incognito mode browsers, assuming they were unique….I wanted to compare separate instances in separate windows

Mr. Genius (Toby) pointed out to me that localStorage is shared amongst Incognito windows – and that was causing things to screw up for me.

This is simple to demonstrate – open up an incognito window

a1

Then go to a site (http://demo.xomino.com) in my case

Open up developer, go to the console and create a localStorage item (.marky in this case)

a2

Then open a new incognito window, go to the same site and check out the Resources, localStorage. As you can see from the image below – the second incognito window has the localStorage values that were created in the first one.

It also appears that cookies are shared between then as well – they are not at all unique.

a4

Good to know !! Bad for my testing

 

PS

I was dared to call this post “multiple porn windows…sharing porn.”…..I resisted 😉

 

 

x$ – now a part of XPages Extension Library

I am very flattered to find out that not only is my x$ OpenNTF xSnippet being used more widely than I realized (over 600 downloads). It now being used in the latest release of the OpenNTF Extension library.

If you look here – http://bootstrap4xpages.com/xsp/.ibmxspres/.extlib/responsive/xpages/js/xsp-mixin.js and search for it you will find


//jQuery selector that works with XPages IDs
//See - http://openntf.org/XSnippets.nsf/snippet.xsp?id=x-jquery-selector-for-xpages
function x$(idTag, param){
	idTag = idTag.replace(/:/gi, "\\:") + (param ? param : "");
	return($("#" + idTag));
}

Who knew my first foray into the XPages community would have such an impact.

The lesson here boys and girls should be that you should *share* your work however small and insignificant you think it is. Like all scientific research, very little code is “a completely new way of doing things”. Generally everything grows incrementally and just because you don’t think something is significant, someone else might.

You are reading this – what have you shared recently?

🙂

 

Got non-XPages design elements? You really should use WebContent.

Recently I was able to help explain an issue Russ Maher was having with his application png files – http://xpagetips.blogspot.com/2014/11/got-png-you-may-get-problems.html. It got me thinking that as modern web developers (which is what XPage developers need to be), we should not be using Domino database elements for “web elements”.

Back before R8 we all used the Database Files, Style sheets and Database Images as a way to reference files as part of the database. I now believe we need to stop using these altogether for “web development”.

w1

WebContent is better

A modern (non-XPages) web based application typically looks like this

w2

As you can see from the image above within a well structured web application css, images and js are all neatly separated out into separate folders. This makes it easy to drag and drop them from web server to web server because all the links are relative and easy to find.

Within our WebContent folder (Accessible within the package manager Window – Open Perspective – XPages) we are able to drag and drop a web project,  jQuery plugin, angular.js directive, extjs and many items and have them continue to function without any issue.

Bootstrap is another great example, well structured and easy to add to our applications.

w3

Source control

When we build out our applications and use source control the “database structure” is stored locally and then committed to our Git or Mercurial repository. Not using the WebContent folder causes us problems when we are searching for files in this environment. Having all our files as it they were in our database makes our lives harder and certainly harder for non-XPages developer to find our code. Some is in “Code” and the rest is in “Resources”, that doesn’t make sense.

w4

 

Conclusion

We as XPages developers need to stop using the “database” for files etc and start to use the WebContent like proper web developers.

 

PS

Yes – I know we all have old applications which get modernized and we cannot always do anything about it, but at the same time for new development moving forward this is where I am at.

 

 

 

XPages and Bluemix: How to create a Websockets message without being connected to the application

In this article I demonstrate how to create a Websocket message to be broadcast, from a website which is not connected to the Websocket directly. This will be achieved by using a http POST request of the message to a node.js server hosted on Bluemix and having that server then emit a Websocket message out to the application.

Introduction

In the previous article I demonstrated how to create an XPages chat client using a Bluemix hosted node.js server. I was able to do this by porting the node.js example to an XPage (xSocket.xsp). What I wanted to figure out (thank you Stack Overflow) was how to trigger a Websocket message to be sent out without using the Websocket socket.io JavaScript client.

Reference articles

This article is based on information already published on the following blog posts:

POSTing to a node route

In the Watson translation service example article I discussed how to create a new POST route within your node application using the simple express construct “app.post”.


var cors = require('cors');
app.use(cors());

var corsOptions = {
  origin: 'http://copper.xomino.com'
};

app.post('/xpages', cors(corsOptions), function(req, res){
  .....
});

In a similar fashion in this example I am going to POST data from the copper.xomino.com website and therefore I need to add the cors code to the application to allow this.

Looking at the message payload

If we look at the client.js file from the original node.js Bluemix example blog post we can see that the message which is sent to the websocket looks like this

    var msgField = $('#msg'),
    data = { msg: msgField.val(), nickname: nickname, when: new Date() };

So our target is to recreate that same data Object and send it through to the server socket emitter in the same format.

Creating the POST data

Once again learning from the previous Watson example the code for this is relatively simple. We create the data Object with a nickname and text value and then POST them to the xominosocket.mybluemix.net site. Logging the response from the server

var data = {
	nickname: 'marky ajax',
	text: 'Sample POST from AJAX'
}
console.log(data)
$.ajax({ url:
	"http://xominosocket.mybluemix.net/xpages",
	type: "POST",
	data: data
}).done(function( msg ) {
	console.log( msg);
});

Modifying the node.js code

The node.js code from the previous article needs to be updated in a number of ways.

The Watson Translation example used a version of express 3.x. Version 4 is more streamlined and does not contain a lot of middleware. When we attempt to POST data to the node server it cannot be understood because it is encoded by the web browser (by default). For his we need to add the npm module body-parser (not forgetting to include it in the package.json).

The new package.json looks like this

{
  "name": "xominosocket",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "start": "node server.js"
  },
  "dependencies": {
    "express": "^4.10.1",
    "jade": "^1.7.0",
    "redis": "^0.10.3",
    "socket.io": "^1.2.0",
    "cors": "2.4.2",
    "body-parser": "1.9.2"
  }
}

The app.post also needs to be able to parse the incoming POST and then re-purpose this into a socket emit event. We do this as follows. I also added a check to make sure the req.body was coming through. This was failing before I realized I needed body-parser.

// Handle the form POST containing the text and nickname
app.post('/xpages', cors(corsOptions), function(req, res){
  var request_data = {};

  console.log(req.body)

  if (req.body){
    request_data = {
      'txt': req.body.text,
      'nickname': req.body.nickname
    };
  } else {
    request_data = {
      'txt': 'this failed',
      'nickname': 'Not this guy'
    };
  }

  var data = { msg: request_data.txt, nickname: request_data.nickname, when: new Date() };
  io.sockets.emit("msg", data);
  res.send(data);
});

Testing the new capability

For the purposes of this article I am only going to show the example in Firebug console. As you can see from the code on the right we are posting to the xominosocket site. On the left you can see the communication was successfully sent to the site, the response and the headers:

ws1

From the next screenshot you can see that the message is them broadcast out to the other client currently connected to the socket, in this case in Chrome.

ws2

Cors security

The cors security only allows POSTing from the copper.xomino.com site. This is a good thing 🙂

ws3

Conclusion

In this article we have seen how we can “create” a websockets emission event on the server by POSTing a message to a separate page within the application.

While this may seem pointless (“because why couldn’t I just create a normal websockets message from the page”), what we will see in a future article is the need to emit an event from the server which is not connected to the Websocket and doesn’t need to be.

 

One way to make a responsive XPages viewPanel

In this article I will demonstrate a method for hiding columns using CSS and making an XPages view Panel pseudo-responsive.

What is Responsive Design?

  1. Responsive design is an approach to web page creation that makes use of flexible layouts, flexible images and cascading style sheet media queries. The goal of responsive design is to build web pages that detect the visitor’s screen size and orientation and change the layout accordingly.

    http://whatis.techtarget.com/definition/responsive-design

Introduction

XPages view panels out of the box are not “responsive”, in that they make no attempt to, and do not work well on a mobile device.

One solution demonstrated by Stewart Curry is to hide columns as the width of the browser decreases. Hiding the least significant, down to the most when the view space is smallest. This makes sense because you would not want to display 9 columns worth of data on a mobile device anyway.

Using the following CSS I was easily able to make an XPages view panel “work” at different resolutions by reducing the number of displayed columns. (IE9 and above http://caniuse.com/#search=nth-child)

<style>
	.xspDataTable {width: 75%}

	@media only screen and (max-width: 1000px) {
		.xspDataTable tr td:nth-child(2), .xspDataTable tr th:nth-child(2) { display:none; visibility:hidden; }
	}

	@media only screen and (max-width: 768px) {
		.xspDataTable tr td:nth-child(3), .xspDataTable tr th:nth-child(3) { display:none; visibility:hidden; }
	}

	@media only screen and (max-width: 480px) {
		.xspDataTable tr td:nth-child(1), .xspDataTable tr th:nth-child(1) { display:none; visibility:hidden; }
	}
</style>

How does it work?
The CSS media queries (IE9 and above) determine the CSS displayed depending on the width of the page (1000px, 768px and 480px in this case). At each stage I remove one column until I am left with just the detail as you can see below.


At > 1000px

a1

At > 768px

a2

At > 480px

a3

and below 480px

a4

Conclusion

This is not the only “method” for trying to achieve responsiveness out of tables, but definitely my favorite 🙂

 

 

 

Creating an XPages Websockets chat client using Bluemix

In this article I will demonstrate how I was able to take an example Bluemix, node.js based, websocket chat program and re-purpose it to be used in XPages.

Introduction

Earlier this year I was very excited to find the Websockets in XPages project on OpenNTF published by Mark Ambler. The concept behind that project is to be able to create a notes document in a queue which is processed and then send out to all users. As much as I promised to help out and use the project, life and a business need to learn and use Angular.js got in the way. My abject apologies to Mark for not following through on my promise to help move the project along.

With this article though, I want to start my exploration of using an external Websockets server to transmit messages to my XPages applications. One of the *nice* things about Websockets is that unlike JavaScript AJAX they are *not* restricted by CORS. This means that I can host my Websockets server anywhere. In this case it is a win win for me as I get to learn more about Bluemix, node.js, websockets and other NoSQL databases like Redis in this case.

Creating a Websockets chat example in Bluemix

I found this article on Create an HTML5 chat app on Bluemix with Node.js, Redis, and Socket.io and following it through I was able to get my own chat program up and running within an hour or so.

ws1

It is not an especially difficult example to follow but I did find that it helped to understand a little about Bluemix and how a node.js application is put together. You should be able to figure it out though just following through the example.

The code for the original example can be found here https://hub.jazz.net/project/joelennon/bluemixchat/overview?welcome= if you want to take a look at it.

Porting the “client” to an XPage

Within the original example, the interface makes a connection to a Redis database to store the last 99 entries of the chat. Within the XPages example I could do that but I am not going to at this time. So the XPages interface will lose chat history when you refresh the page. I am not really concerned about that in the big picture.

Because I had all the files locally I was able to create a new IBM Domino database and drag the files into the WebContent directory. Within node.js the “Public” directory is assigned to the root of the server, but in this case I removed the public folder as it is unnecessary.

w2s2

 

The original example uses the jade templating engine to create the web page. But in this case I felt lazy and just viewed the source of the example once it was working and then extracted all the HTML I needed.

Moving socket.io to the HEAD

Because this is XPages and because we have dojo and I am sure I have pointed out before – we have to move the files to the HTML HEAD in such a manner as they come before Dojo within the application. Socket.io is apparently one such JavaScript library.

The HTML of the XPage is relatively simple. As you can see below we a using the xp:resources tag to insert the same HTML references to the local files as they were in the original example.

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">

	<link rel="stylesheet" href="//cdn.jsdelivr.net/normalize/3.0.1/normalize.min.css"/>
	<link rel="stylesheet" href="stylesheets/style.css"/>

	<xp:this.resources>
		<xp:headTag tagName="script">
			<xp:this.attributes>
				<xp:parameter name="type" value="text/javascript" />
				<xp:parameter name="src" value="//cdn.jsdelivr.net/jquery/2.1.1/jquery.min.js" />
			</xp:this.attributes>
		</xp:headTag>
		<xp:headTag tagName="script">
			<xp:this.attributes>
				<xp:parameter name="type" value="text/javascript" />
				<xp:parameter name="src" value="socket.io/socket.io.js" />
			</xp:this.attributes>
		</xp:headTag>
		<xp:headTag tagName="script">
			<xp:this.attributes>
				<xp:parameter name="type" value="text/javascript" />
				<xp:parameter name="src" value="javascripts/client.js" />
			</xp:this.attributes>
		</xp:headTag>
	</xp:this.resources>

	<h1>XPages Websocket Chat using Bluemix</h1>

	<input id="msg" autocomplete="off" autofocus="autofocus"/>
	<button type="submit">Send</button>

	<ul id="messages">
	</ul>

</xp:view>

 The critical difference – the server connection

Within the original example the socket code was on the same server as the client creating the messages. In this case they are not as my XPages server is wholly independent of Bluemix.

So I had to change the initial connection to the websocket server. Within the client.js file I changed the first line from

$(document).ready(function() {
  var socket = io(), nickname, msgList = $('#messages');
  ...

to the following

$(document).ready(function() {
  var socket = io.connect('http://xominosocket.mybluemix.net') //connect to the Bluemix server
  var nickname, msgList = $('#messages');
  ...

With these changes and a couple of stupid spelling mistake corrections I was able to bring my application up within my xSocket XPage.

ws3

You can see from the Firebug console that the copper.xomino.com application is talking to the xominosocket.bluemix.net application

Mobile Compatible

Yes you have to be connected to the website (rather than OS Push notification) but websockets works on iOS7+ and Android 4.4+

(http://caniuse.com/#feat=websockets)

Screenshot_2014-11-08-11-47-45

Conclusion

There is a lot more detail which we can go into as to how this example works but in the mean time if you want to play with it.

Here is the Bluemix Page: http://xominosocket.mybluemix.net/ (with chat history)

Here is an XPage: http://demo.xomino.com/xomino/WSinX.nsf/xSocket.xsp (no history)

For the full effect – open each link in a different browser and you can talk to yourself 😉

You can find my Bluemix code on Jazz Hub – https://hub.jazz.net/project/mdroden/xominosocket/overview.