Pushing data to an XPage from the server – HTML5 eventSource

Introduction

We are going to look at the new HTML5 eventSource capability. Adapting the articles (posted here) and (posted here) I am goign to show you how to send data from the server to your XPage without using ajax to initiate the communication. I have the feeling there is a lot to learn in this area and we are just scratching the surface.

Browser Support

Yep – it is HTML5 which means no IE right now – Chrome should support this but my copy does not – so right now this demonstration is only tested and working in the latest version of FireFox.

Demonstration

The results of this article are demonstrated here, and I strongly suggest you use some kind of plugin to watch the traffic as the values chance – quite fascinating. Right click – View Source and see for yourself – no ajax! no jQuery, nothing fancy, just HTML5 in action!

http://demo.xomino.com/xomino/xPlay.nsf/xHTML5ServerSent.xsp

Server output

I have modified Stephan Wissel’s xAgent XSnippet to create a simple data stream using server-side output. Calling this xAgent through the web generated the following simple data output as seen here through Firefox HTTPFox plugin.

The thing to highlight is the Content-Type text/event-stream

xAgent output
xAgent output
<?xml version="1.0" encoding="UTF-8"?>
<!-- XPage which is not rendered but returns data like XML, JSON, etc.     -->
<!-- More: http://www.wissel.net/blog/d6plinks/shwl-7mgfbn                 -->

<xp:view xmlns:xp="http://www.ibm.com/xsp/core" rendered="false">
  <xp:this.afterRenderResponse>
	<![CDATA[#{javascript:
		var externalContext = facesContext.getExternalContext();
		var writer = facesContext.getResponseWriter();
		var response = externalContext.getResponse();

		// set content type, e.g. ...
		response.setContentType("text/event-stream");

		response.setHeader("Cache-Control", "no-cache");

		// read parameters, e.g. ...
		var param = context.getUrlParameter("myParam");

		// write HTML output e.g. ...
		writer.write("data: the number is "+@Random());

		writer.endDocument();
	}]]>
  </xp:this.afterRenderResponse>
</xp:view>

HTML5 JavaScript

In our xPage we create some JavaScript to instantiate and then listen to the eventSource
First of all we test to see if the EventSource is supported in this browser

		if(typeof(EventSource)!=="undefined") {

		else {
			dojo.byId("#{id:serverData}").innerHTML="Whoops! Your browser doesn't receive server-sent events.";
		}]]>

Using the following code we subscribe to the EventSource (xSendHTML5Data_1.xsp is the xPage described above)

			var eSource = new EventSource("xSendHTML5Data_1.xsp");

And then when we receive a message from the server we act on it

			eSource.onmessage = function(event) {
				//write the received data to the page
				dojo.byId("#{id:serverData}").innerHTML = event.data;
			};

This all comes together as the following ScriptBlock in the page

	<xp:scriptBlock id="scriptBlock1">
		<xp:this.value><![CDATA[//check for browser support
		if(typeof(EventSource)!=="undefined") {
			//create an object, passing it the name and location of the server side script
			var eSource = new EventSource("xSendHTML5Data_1.xsp");
			//detect message receipt
			eSource.onmessage = function(event) {
				//write the received data to the page
				dojo.byId("#{id:serverData}").innerHTML = event.data;
			};
		}
		else {
			dojo.byId("#{id:serverData}").innerHTML="Whoops! Your browser doesn't receive server-sent events.";
		}]]>
		</xp:this.value>
	</xp:scriptBlock>

Stopping the source

We are able to stop the source using eSource.close() that will stop the data being sent. I added mine in a button

	<xp:button id="button1" value="Stop the Source">
		<xp:eventHandler event="onclick" submit="false">
			<xp:this.script><![CDATA[eSource.close()]]></xp:this.script>
		</xp:eventHandler></xp:button>

Changing the refresh time

We are able to change the refresh time by returning the retry value in millisecond along with our data

writer.write("retry: 2000\ndata: the number is "+@Random());
setting the refresh time 2000ms
setting the refresh time 2000ms

Specifying an event name

This mercilessly ripped directly from the article – I have not done anything with the event listeners yet, but here the next thing I want to look at….using the data returned from the server to trigger events in the browser without having to parse the return string and take action on it – that’s a huge performance gain and distributes the tasks….more to come on that in the near future….

For example, the following server output sends three types of events, a generic ‘message’ event, ‘userlogon’, and ‘update’ event:

data: {"msg": "First message"}\n\n
event: userlogon\n
data: {"username": "John123"}\n\n
event: update\n
data: {"username": "John123", "emotion": "happy"}\n\n
With event listeners setup on the client:

source.addEventListener('message', function(e) {
  var data = JSON.parse(e.data);
  console.log(data.msg);
}, false);

source.addEventListener('userlogon', function(e) {
  var data = JSON.parse(e.data);
  console.log('User login:' + data.username);
}, false);

source.addEventListener('update', function(e) {
  var data = JSON.parse(e.data);
  console.log(data.username + ' is now ' + data.emotion);
}, false);

Enjoy 🙂

Demonstration

http://demo.xomino.com/xomino/xPlay.nsf/xHTML5ServerSent.xsp

Advertisements

9 thoughts on “Pushing data to an XPage from the server – HTML5 eventSource

  1. Interesting but, as far as I know, current domino http / java server side stack is not tailored to scale with such technologie (same for websocket). It works but it can not scale. As far as I know, we need some special server side stuff (Node.js, Jetty etc…) to really take advantage of those new web requests models

    • That is a very good point – but then we are talking Domino Servers here – and scalability has never been the driving factor. It is good though to start the conversation early so that if necessary IBM keep an open mind to it in the future

  2. This post leaves me with the feeling I first had when I realized (or had explained to me) what xmlhttprequest could do. What other strange and wonderful creatures are going to come out of the HTML5 woodwork over the next years as developers like you push its capabilities? By the way, you’ve got a really great blog and I’ll be following it. Nice to meet you Wed. at the DCLUG.

    • Thanks for the support Harris 🙂 – good to meet you too. I plan to be more involved in DCLUG than I was in the past.

      As far as HTML5 goes, it has been around for 2 years now but there really hasn’t been the browser support for it. It is so much fun to get inside it and play – so much to learn and so little time to do it 🙂

  3. To get this working in Chrome you need to end every line in which you send the data with two ‘\n’ characters:

    writer.write(“data: the number is “+@Random() + “\n\n”);

  4. How do you update the stream from the server ?
    For example, my user A save a new document on my database A
    In my database A exist the “xSendHTML5Data_1.xsp”.
    How do you do so that the Xpage (“xSendHTML5Data_1.xsp”) make the calculation about if there is a change -aka a new document-?
    so that it can change the message and send the update over the stream to the browser (a mobile device in reality).

    -The Xpage will have to be asking to the server ALL the time if there is a change ?
    -Server will be loaded with the extra work of checking if there is a change all the time ?

    -Is it usable in production conditions ?
    I mean in my case the server handle many NSF and I do not want that 10 users overload the http task with infinite loop of checking

    Thanks for sharing

  5. Thanks for the comment Dominique 🙂

    Having had multiple discussions with people since I wrote the article I think it is something to be consigned to the “interesting but not helpful bin”. You are right there is nothing in the domino stack to know that there is a change and send it without constant polling.

    We have been actively pushing the DDE development team to add websockets to the product and I think/hope they are starting to listen. Once we have that then we go from interesting to practical.

    In the mean time here is an interesting stackoverflow comparison between the two

    http://stackoverflow.com/questions/5195452/websockets-vs-server-sent-events-eventsource

    • I am about to check if we can use one variable with application scope level from the xpages.
      So the xpage who sent the data will check that.
      And the workflow will rely on on the variable and a sleep function

      state 1: global variable – no update – the xpage does not send data
      state 2: the user trought agent change the value of the global variable – send data
      the xpage send data
      then an agent with a sleep function, change the global variable to “no update” after 10 to 30 seconds (value to test).

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s