Sunday, 13 December 2009

StreamHub Comet Server 2.0.11 Release

This minor release of StreamHub Reverse Ajax & Comet Server brings with it a few bug fixes and full Unicode support. The major changes are listed below:

  • Unicode support for international charsets.
  • Fixes for the connection listener in Firefox 2+ & 3+.

Get the latest version

Find the latest version available to download below aswell as links to the most up-to-date documentation and tutorials:

More coming soon, as usual, subscribe to this blog or follow us on twitter to keep up to date.

Thursday, 26 November 2009

StreamHub 2.0.10 Released. New Demos & Connection Listener!

We've just released a new version of the StreamHub Reverse Ajax & Comet Server. We've added a whole bunch of new stuff, details below:
  • New jQuery Grid Plugin Demos
  • New Example Fixed Income streaming data
  • New Ext-GWT Demo
  • Fix for the 'queue is full - disconnecting' loop bug
  • Clients who recently lost connection and are in the reconnection state now do not count towards the maximum user limit
  • StreamHub now distinguishes between deliberate user disconnects and clients losing connection in the logs
  • A connection listener has been added to the Ajax SDK
Using the new Ajax SDK Connection Listener

To use the new connection listener, you'll first need to create your own custom listener code. For example, to alert everytime the connection goes up or down, add the following JavaScript to your host page:

var connectionListener = new StreamHub.ConnectionListener();
connectionListener.onConnectionEstablished = function () {
alert("Connection up");
};
connectionListener.onConnectionLost = function () {
alert("Connection down");
};

Then, before you connect, just add your new connection listener:

var hub = new StreamHub();
hub.addConnectionListener(connectionListener);
// now connect as normal...

You can add as many connection listeners as you like. The alerting example is pretty simple, you will probably want to do something more useful such as add a connection monitor on your page which is green when the connection is up and red when its down. Lets go ahead and add such a connection monitor to one of the examples which come with the StreamHub SDK. Navigate to the examples\StockDemo directory of the SDK and edit the index.html file. Add the following div element just above the closing body tag:

<div id="connectionMonitor" style="width: 120px; border: 1px black solid; background-color: yellow">Not connected</div>

Then change the code between the last script tags to the following:

var connectionListener = new StreamHub.ConnectionListener();
connectionListener.onConnectionEstablished = function () {
var connectionMonitor = document.getElementById('connectionMonitor');
connectionMonitor.innerHTML = "Connected";
connectionMonitor.style.backgroundColor = "green";
};
connectionListener.onConnectionLost = function () {
var connectionMonitor = document.getElementById('connectionMonitor');
connectionMonitor.innerHTML = "Disconnected";
connectionMonitor.style.backgroundColor = "red";
};

var hub = new StreamHub();
hub.addConnectionListener(connectionListener);
var sServerUrl = "http://localhost:7979/";
hub.connect(sServerUrl);
hub.subscribe("BA", priceChangeListener);
hub.subscribe("BAC", priceChangeListener);
hub.subscribe("C", priceChangeListener);
hub.subscribe("KO", priceChangeListener);
hub.subscribe("MCD", priceChangeListener);
hub.subscribe("WMT", priceChangeListener);

Open the StockDemo at http://localhost:7979/StockDemo/index.html and you should see the connection monitor in action. See the screenshot below for an example of the finished item:



New examples overview

We've added three new examples to this version. Two show how to use the versatile jQuery Grid Plugin. The first just displays a simple grid of stock prices.


The second uses the new example Fixed Income data. It shows how to use a larger grid, how to use a different theme and how to highlight rows using the 'ui-hover' class so the row highlight is tied to the jQuery UI theme.



The third new example uses Ext GWT to create a simple sortable grid with custom green/red formatters and the option to hide/show columns.



The full source code for the Ext GWT demo is available from the GWT Comet Adapter source website. The main classes are ExtGwtApp.java and Stock.java.


Get the latest version

Find the latest version available to download below aswell as links to the most up-to-date documentation and tutorials:

Enjoy! More demos are coming soon, subscribe to this blog or follow us on twitter to keep up to date.

Thursday, 29 October 2009

StreamHub 2.0.9 Released. New YUI Comet Demo!

A new version of the core StreamHub server has been released. In this version we've added YUI integration, providing a new demo of how to integrate Comet & Reverse Ajax in to your YUI applications. Click the screenshot below to see the new demo in action:



Grab the latest release and browse the API docs below:

Enjoy! More demos are coming soon, subscribe to this blog or follow us on twitter to keep up to date.

.NET Thick Client SDK 1.0.1.22 Maintenance Release

A new version of the .NET Thick Client SDK has been released. The language-neutral .NET Thick Client SDK allows you to connect rich .NET-based GUIs to the core StreamHub server using your preferred technology choice including WPF, C#, VB and C++.

This maintenance releases brings the compatibility level down to .NET Framework 2.0 (previously requiring 2.0 SP2). The SDK is available to users of the Web and Enterprise editions of StreamHub (request a free, no-obligation 60-day evaluation). It provides an alternative desktop-based platform for server push applications, allowing clients to connect both over the web, via the Ajax SDK, or from their desktop via the Thick Client SDK. The Thick Client SDK is also available in Java.

Thursday, 1 October 2009

Getting Started with Reverse Ajax and Comet

In this article I'll be covering what Reverse Ajax and Comet are, how to use them, and how to get up and running with an example in no time at all.

Reverse Ajax and Comet

Reverse Ajax and Comet are essentially the same thing - long-lived HTTP requests allowing your browser to receive real-time events from the server without refreshing the page or installing any plugins. Chances are you use Comet every day without even realizing it: GMail use it for displaying new e-mails as they're received, Facebook uses it for chat and friend updates, last.fm will be using it to show you what your friends are currently listening to, basically its pretty widespread in Web 2.0 land.

Quick Overview of the Innards of Reverse Ajax and Comet

Initially maligned as a 'hack' by some, Comet has since matured, turning itself into the protocol du jour for Web 2.0. Implemented under the covers via long-polling XMLHttpRequests, hidden iframes, Bayeux, or HTML 5 it all works over HTTP - either keeping a connection open to the server or polling the server for the most recent updates. Luckily there are plenty of servers around now that hide all the nitty gritty details away and wrap them up in a reliable publish/subscribe framework.

Getting Started - 'Hello World' in Comet and Reverse Ajax

The first example we'll be creating will be the obligatory 'Hello World!'. For this example grab yourself a copy of the free Community Edition of StreamHub Reverse Ajax & Comet Server. This will limit us to 20 users but that should be plenty for our purposes. If you need more, you can request the free 60-day evaluation. To get started, create a directory called CometTutorial or similar, and create a new file helloworld.html, containing the following HTML:



<head>
<title>Hello World Comet Application</title>
<script src="streamhub-min.js" type="text/javascript"></script>
</head>
<body>
<h1>Hello World Comet Application</h1>
<input type="button" value="Say hello" onclick="start()">
<div id="streamingData"></div>
<script>
function topicUpdated(sTopic, oData) {
var newDiv = document.createElement("DIV");
newDiv.innerHTML = "Update for topic '" + sTopic + "' Response: '" + oData.Response + "'";
document.getElementById('streamingData').appendChild(newDiv);
}

function start() {
var hub = new StreamHub();
hub.connect("http://localhost:7878/");
hub.subscribe("HelloWorld", topicUpdated);
}
</script>
</body>
</html>


All we've done here is create a simple HTML host page for our 'Hello World' app and added a bit of code to the onclick of the 'Say hello' button to connect to the Comet server and subscribe to a topic, 'Hello World'. Notice the topicUpdated function is never called but instead passed into the subscribe function. The StreamHub Ajax API will call the function every time a new update is received from the server. Inside the topicUpdated function we create a new div and append it to the page to show the contents of the new event from the server. With the client-side done, we'll now need to create the server-side.

Create a new file called HelloWorld.java containing the following code:



import java.io.File;
import com.streamhub.api.PushServer;
import com.streamhub.nio.NIOServer;
import com.streamhub.api.Client;
import com.streamhub.api.SubscriptionListener;
import com.streamhub.api.JsonPayload;

public class HelloWorld implements SubscriptionListener {

public static void main(String[] args) throws Exception {
new HelloWorld();
}

public HelloWorld() throws Exception {
PushServer server = new NIOServer(7878);
server.addStaticContent(new File("."));
server.start();
server.getSubscriptionManager().addSubscriptionListener(this);
System.out.println("Comet server started at http://localhost:7878/.");
System.out.println("Press any key to stop...");
System.in.read();
server.stop();
}

public void onSubscribe(String topic, Client client) {
JsonPayload payload = new JsonPayload(topic);
payload.addField("Response", "Hello World!");
client.send(topic, payload);
}

public void onUnSubscribe(String topic, Client client) {
// no need to do anything here
}
}


In this bit of code we create a new PushServer, add the current directory as static content so we can serve up our helloworld.html page, and start 'er up.

Its important to note at this point that for a production app you'll want to server your static files from a separate HTTP server such as Apache, IIS or Tomcat. StreamHub is engineered for highly-scalable Comet & Reverse Ajax and although it can serve your static content, its not the primary purpose. The most typical setup is to serve static content via your usual HTTP server from www.<domain>.com and real-time content via StreamHub from push.<domain>.com.

The key part of the code is in the onSubscribe function, here we reply to any subscription requests from the client by sending them a JsonPayload containing our 'Hello World' message. Now we've created the server and client, we'll need to copy some files over from the StreamHub download and compile everything. If you haven't yet, download StreamHub Reverse Ajax & Comet Server, and extract the archive (there's a big download button on the website - you can't miss it). Inside the streamhub-java-adapter-sdk folder you should see something like this:




Copy the conf folder and the license.txt file over to the same folder our HelloWorld app is located in. This will stop StreamHub moaning about licenses and logging when we start it up later. Now navigate into the examples/lib/jar directory and copy over streamhub-2.0.8.jar, log4j-1.2.14.jar and json-20080701.jar to the same place as before. While you're there, copy streamhub-min.js from examples/lib/js. You should end up with a folder which looks something like this:




Now to compile, you'll need Java 5 or greater, if you don't have it, get it here (Download the latest Java SE Development Kit - JDK 6 at time of writing). To compile, open a command prompt in the HelloWorld directory and run:


javac -cp streamhub-2.0.8.jar HelloWorld.java

To start the server run (Windows):


java -cp .;streamhub-2.0.8.jar;log4j-1.2.14.jar;json-20080701.jar HelloWorld


On Unix, you'll need to replace all ';' with ':':


java -cp .:streamhub-2.0.8.jar:log4j-1.2.14.jar:json-20080701.jar HelloWorld

Now open a browser to http://localhost:7878/helloworld.html and click the 'Say hello' button to see it in action. You should see something like this (I clicked it a few times):




There we go! 'Hello World' for Reverse Ajax & Comet in a few lines of Java and JavaScript, no complicated configuration required. However, if you're like me, 'Hello World' is never enough and to be honest this simple app hardly shows off Comet - so lets create something a bit more exciting.

Getting Advanced - Reverse Ajax & Comet Stock Ticker

In this example, we're going to create a stock ticker table, a bit like the one on the StreamHub homepage. To start, create a file called stockticker.html in the same folder as the HelloWorld example. Add the following HTML to this file:



<head>
<title>Stock Ticker Comet Application</title>
<script src="streamhub-min.js" type="text/javascript"></script>
</head>
<body>
<h1>Stock Ticker Comet Application</h1>
<input type="text" value="AAPL" id="stockSymbol">
<input type="button" value="Subscribe" onclick="subscribe()">

<table id="tickerTable">
<tr><th align="left" width="150">Symbol</th><th align="left" width="100">Last Price</th></tr>
<tr><td>GOOG</td><td id="GOOG_Price">-</td></tr>
<tr><td>MSFT</td><td id="MSFT_Price">-</td></tr>
</table>

<script>
function topicUpdated(sTopic, oData) {
var priceDiv = document.getElementById(sTopic + "_Price");
priceDiv.innerHTML = oData.Last;
}

function subscribe() {
var topic = document.getElementById('stockSymbol').value;
var rowEl = document.createElement('tr');
var symbolEl = document.createElement('td');
symbolEl.innerHTML = topic;
var priceEl = document.createElement('td');
priceEl.id = topic + "_Price";
rowEl.appendChild(symbolEl);
rowEl.appendChild(priceEl);
tickerTable.appendChild(rowEl);
hub.subscribe(topic, topicUpdated);
}

var tickerTable = document.getElementById('tickerTable');

var hub = new StreamHub();
hub.connect("http://localhost:7878/");
hub.subscribe("GOOG", topicUpdated);
hub.subscribe("MSFT", topicUpdated);
</script>
</body>
</html>


This is a little bit more complicated than the 'Hello World' example. Here, we define a table in the HTML markup and give two of the cells the IDs of GOOG_Price and MSFT_Price. We then subscribe to GOOG and MSFT passing in a topicUpdated function as before. Now, in this topicUpdated, we find an element prefixed by topic and ending in _Price, so when we receive an update for topic GOOG, the element with ID GOOG_Price is updated with the latest price received from the server. Essentially whenever we receive an update from the server, the table is automatically updated with the new price. You'll notice we've added a textbox and a button to subscribe to a ticker symbol. This button calls the subscribe function and dynamically adds a new row to the table and subscribes to the topic via the hub. Now onto the server-side code.

Create a new file called StockTicker.java, this will provide random price events to our client. Insert the following code:



import java.io.File;
import com.streamhub.api.PushServer;
import com.streamhub.nio.NIOServer;
import com.streamhub.api.Client;
import com.streamhub.api.SubscriptionListener;
import com.streamhub.api.JsonPayload;
import java.util.Set;
import java.util.HashSet;
import java.util.Random;
import java.text.NumberFormat;
import java.text.DecimalFormat;

public class StockTicker implements SubscriptionListener {
private final Random random = new Random();
private final NumberFormat priceFormatter = new DecimalFormat("0.00");
private final Set symbols = new HashSet();
private PushServer server;


public static void main(String[] args) throws Exception {
new StockTicker();
}

public StockTicker() throws Exception {
server = new NIOServer(7878);
server.addStaticContent(new File("."));
server.start();
server.getSubscriptionManager().addSubscriptionListener(this);
new Thread(new RandomStockTicker()).start();
System.out.println("Comet server started at http://localhost:7878/.");
System.out.println("Press any key to stop...");
System.in.read();
server.stop();
}

public void onSubscribe(String topic, Client client) {
JsonPayload payload = new JsonPayload(topic);
payload.addField("Last", "0.00");
client.send(topic, payload);

synchronized(symbols) {
symbols.add(topic);
}
}

public void onUnSubscribe(String topic, Client client) {
// no need to do anything here
}

private class RandomStockTicker implements Runnable {
public void run() {
while(server.isStarted()) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}

synchronized(symbols) {
for (String symbol : symbols) {
double nextPrice = random.nextDouble() * 100.0;
JsonPayload payload = new JsonPayload(symbol);
payload.addField("Last", priceFormatter.format(nextPrice));
server.publish(symbol, payload);
}
}
}
}
}
}


In the onSubscribe method we send a direct response to the client as we did in the 'Hello World' example, but we also add the client's subscription to a Set (unique list) of symbols. You'll notice in the constructor we kicked off a new Thread called RandomStockTicker. The RandomStockTicker simply loops through the list of symbols in the background and generates a random price for each one. It will continue to loop, sleeping for 1000ms between each burst of updates. Time to compile and run.

You should have all the files you need from the 'Hello World' example, leaving your folder looking something like this:



To compile, run the following:



javac -cp streamhub-2.0.8.jar StockTicker.java


And to run (Windows):



java -cp .;streamhub-2.0.8.jar;log4j-1.2.14.jar;json-20080701.jar StockTicker

On Unix:



java -cp .:streamhub-2.0.8.jar:log4j-1.2.14.jar:json-20080701.jar StockTicker


Now open up a browser to http://localhost:7878/stockticker.html and check it out! Because we used the publish method, the updates will be sent to all subscribed clients - so if you open up a few browsers you can see the same updates being delivered to multiple clients. Below I've opened it in Firefox, IE, Comet and Safari:




That's it for now. If you'd like to take it further, try adding green and red highlighting to the prices depending on whether they've gone up or down (Tip: check out the jQuery Highlight Effect). If you're looking for a more trick table or grid with sorting/filtering etc.., try ExtJS, YUI or the jQuery Grid Plugin. Remember to subscribe to the blog for the latest tutorials, releases and news.


Thursday, 24 September 2009

StreamHub Comet Server 2.0.7 Released - New Real-Time Chart Demo

A new version of the StreamHub Push Server has just been released. In this latest release, 2.0.7, we've added a new real-time chart demo, showing how to use the jQuery Flot Plugin to update charts via Comet. The example makes use of excanvas in Internet Explorer to make it fully cross-browser compatible with Chrome, Safari, IE, Firefox et. al.

Click the screenshot below to see it in action:



We think real-time charting through pure JavaScript is really exciting and are looking forward to producing a whole bunch of cool demos in future.

Tuesday, 22 September 2009

Comet in GWT with StreamHub - New Release

A new version of the GWT Comet Adapter has been released for StreamHub Comet Server. The adapter is a GWT module which when included in your project allows you to stream real-time data from StreamHub to your GWT apps. Click the screenshot below to see a demo of the adapter in action:



In this latest release full failover and reconnection support has been added. Using the new connect method, its now possible to configure 100% of the failover and reconnection options available in StreamHub. For example:

StreamHubGWTAdapter streamhub = new StreamHubGWTAdapter();
List servers = new ArrayList();
servers.add("https://push1.stream-hub.com/");
servers.add("https://push2.stream-hub.com/");
servers.add("https://push3.stream-hub.com/");
FailoverParameters failoverParameters = new FailoverParameters(servers);
failoverParameters.setAlgorithm(FailoverAlgorithm.PRIORITY);
failoverParameters.setInitialReconnectDelayMillis(2000);
failoverParameters.setMaxReconnectDelayMillis(30000);
failoverParameters.setUseExponentialBackOff(true);
failoverParameters.setBackOffMultiplier(2.0);
failoverParameters.setMaxReconnectAttempts(20);
streamhub.connect(failoverParameters);

For full details of the API, you can browse the Javadoc online or checkout the projects homepage on Google Code.

Friday, 18 September 2009

StreamHub Comet Works Great on Latest iPhone - No Plugins Required


We've just been trying out StreamHub Comet on the latest iPhone 3GS - it works really smoothly without installing anything, no plugins or Flash required, it works out the box. Try it yourself, just browse to the StreamHub website on your iPhone and check out the real-time Comet goodness.

Tuesday, 15 September 2009

Configuring Failover and Reconnection with the StreamHub JavaScript API

Introduction

The latest version of StreamHub Comet Server (2.0.6) includes the ability to configure failover and reconnection in the JavaScript API. This article covers the basics of configuring a StreamHub connection to use failover. Which scripts to include and how to get started with StreamHub are not covered in this article. If you're new to StreamHub I recommend you start with the Getting Started Guide.

The New Connect Method

The connect method is now overloaded to take two types of argument, a string URL, or a JavaScript object with the failover configuration parameters. First of all, the new method is completely backwards compatible, so you will not need to change any code like below, which uses the old method of connecting:


var hub = new StreamHub();
hub.connect("http://push.example.com/");

The above configures a connection with no failover, but it does have, by default, reconnection behaviour. The default reconnection behaviour is upon connection loss to attempt to reconnect to the URL every second until the connection is re-established. This provides a quick and seamless reconnection for when the server becomes reachable again. If this isn't what you need, you'll want to make use of the new connect method. The example below uses the exact same method, but this time we pass in an anonymous JavaScript object specifying different reconnection and failover behaviour:


var hub = new StreamHub();
hub.connect({
serverList: ["http://primary.example.com/",
"http://secondary.example.com/"],
failoverAlgorithm: "ordered",
initialReconnectDelayMillis: 2000
});

Firstly, in the configuration object we specify a serverList as an array of string URLs. This is a list of StreamHub servers to which a connection can be made. The first server in the list will always be the one which is connected to first. If the connection is the lost, the failoverAlgorithm option specifies which server to try next. In this case, we use "ordered", which means proceed down the serverList in order, attempting to connect to each server in turn until a connection is successfully established. Finally, the initialReconnectDelayMillis specifies how long to wait between each reconnection attempt. Here we specify 2000 ms (2 seconds). For reference, all of these configuration options and more are documented in the JSDOC for StreamHub.connect(config).

Exponential BackOff and Limiting the Number of Reconnections

Now for a more advanced example demonstrating exponential backoff, a feature you may have seen in some JMS implementations:
var hub = new StreamHub();
hub.connect({
serverList: ["https://push1.example.com/",
"https://push2.example.com/",
"https://push3.example.com/"],
failoverAlgorithm: "ordered",
initialReconnectDelayMillis: 1000,
maxReconnectDelayMillis: 30000,
useExponentialBackOff: true,
backOffMultiplier: 2
});
In the above example, we've introduced a backOffMultiplier and a maximum reconnection delay. The back off feature allows you to increase the wait between each reconnection attempt over time, in this case by a multiple of 2. The best way to demonstrate how this works is with an example:

  1. Client successfully connects to https://push1.example.com/

  2. Client loses connection

  3. Client waits 1000ms

  4. Client fails to reconnect to https://push2.example.com/

  5. Client waits 2000ms

  6. Client fails to reconnect to https://push3.example.com/

  7. Client waits 4000ms

  8. Client fails to reconnect to https://push1.example.com/

  9. ...

This will continue until the wait time reaches 30000ms, since this is the maxReconnectDelayMillis we specified. The reconnection attempts will still continue but with a wait time of 30000ms. To limit the number of reconnection attempts we must use the maxReconnectAttempts option, this specifies a maximum number of reconnects to attempt before giving up:

var hub = new StreamHub();
hub.connect({
serverList: ["https://push1.example.com/",
"https://push2.example.com/",
"https://push3.example.com/"],
failoverAlgorithm: "ordered",
initialReconnectDelayMillis: 1000,
maxReconnectDelayMillis: 30000,
maxReconnectAttempts: 30,
useExponentialBackOff: true,
backOffMultiplier: 2
});
That's it, a quick overview of the reconnection and failover configuration available in the StreamHub JS API. As mentioned earlier in the article, for detailed coverage of all the options, refer to the JSDOC for StreamHub.connect(config).


StreamHub Push Server 2.0.6 Released

A new version of the core StreamHub HTTP Push Server has just been released. This is another minor release improving the quality, logging, and introducing a couple of new resilience and benchmarking features. There's a new mechanism for attaching timestamps to messages, comprehensive failover and reconnection configuration for the JavaScript API, and a general streamlining of the core server logging. Oh, and all utility Javascript classes have now been namespaced under StreamHub to avoid clashes.
As always you can find our getting starting guides here on the blog:

Friday, 7 August 2009

Loading the License and Log4J Configuration from an Alternative Location

This article covers how to load the license and Log4J configuration from a non-default location in StreamHub Push Server version 2.0.3 or later.

Default locations

The default location for the license is a file called license.txt in the current working directory or ".". The default location for the Log4J configuration is conf/log4j.xml relative to the current working directory.

Loading from a different location

In order to load the license and Log4J configuration from a different location, we'll need to make use of a different constructor when instantiating the server. Looking at the Javadoc, we find this constructor:

public NIOServer(InetSocketAddress address,
InetSocketAddress streamingAdapterAddress,
URL licenseUrl,
URL log4jConfigurationUrl)
This constructor allows us to specify a URL for the license and logging configuration. The Javadoc also tells us the types of URL we can specify:

  • File URLs e.g. file:///C:/licenses/license.txt
  • Remote URLs e.g. http://www.example.com/logging/log4j.xml
  • JAR URLs e.g. jar:file:///C:/lib/streamhub-license.jar!/license.txt or jar:http://www.example.com/logging/streamhub-logging.jar!/log4j.xml
  • Classpath URLs via com.streamhub.util.UrlLoader e.g. UrlLoader.load("classpath:/license.txt"), or UrlLoader.load("classpath:/conf/log4j.xml")
As an example, the below starts a server as normal on port 80, loading the license and logging configuration from the classpath. This assumes we have prod.license and prod-logging.xml available on the classpath, perhaps through another JAR.

URL licenseUrl = UrlLoader.load("classpath:/prod.license");
URL loggingUrl = UrlLoader.load("classpath:/prod-logging.xml");
PushServer server = new NIOServer(new InetSocketAddress(80), null, licenseUrl, loggingUrl);
Note: we pass null through as the second parameter to indicate we don't want to start a streaming adapter. Streaming adapters are used to push updates through the server from remote locations and aren't covered in this article. Also, note we make use of the com.streamhub.util.UrlLoader to generate the URLs as classpath resources aren't supported by default in Java.

The second example below loads the files from a remote location, this would usually be an internal location only visible to the server and not accessible by the outside world.

URL licenseUrl = new URL("http://staging1.example.com/streamhub/license-staging.txt");
URL loggingUrl = new URL("http://staging1.example.com/streamhub/log4j-staging.xml");
PushServer server = new NIOServer(new InetSocketAddress(80), null, licenseUrl, loggingUrl);
The final example loads the files locally from the filesystem:

URL licenseUrl = new URL("file:///C:/prod/license.txt");
URL loggingUrl = new URL("file:///C:/prod/log4j.xml");
PushServer server = new NIOServer(new InetSocketAddress(80), null, licenseUrl, loggingUrl);
That last example brings this tutorial to an end. We've covered the default locations of license.txt and conf/log4j.xml and how to override them by passing URL arguments to the four parameter constructor. For more detail on the various constructors available, refer to the Javadoc online. More detail on the XML that goes in the Log4J configuration file can be found in the Log4J XML Configuration Primer.

StreamHub Push Server 2.0.3 Released

A new version of the core StreamHub Push Server has just been released. This is a minor release to improve the quality, configurability and documentation. There's a new constructor for specifying alternate locations for the license and log4j configuration. (More info in this blog article). And with this release we've consolidated and published all the documentation externally. Follow the links below for the latest developments:

Wednesday, 5 August 2009

StreamHub GWT Comet Adapter 1.0.2 Released

A new version of the StreamHub GWT Comet Adapter has just been released. It now features full publish/subscribe support bringing it 100% in to line with the StreamHub API. Accompanying this new release is a comprehensive set of Javadocs available to browse online or download as a JAR. Follow the links below:

Wednesday, 22 July 2009

Get Help in our New StreamHub Comet Google Group

We've launched a new Google Group as a place where StreamHub users can seek help, get support, and ask any questions about StreamHub or Comet in general.

The StreamHub Team will be monitoring the group to answer any questions you may have. Visit us using the link below:


Tuesday, 21 July 2009

Tutorial: Building a Comet Chat Application with StreamHub

Newly updated for version 2.0.2. Download 2.0.2 here.

Introduction

This tutorial will guide you through how to create a simple Comet chat application using the StreamHub Comet server. StreamHub is a lightweight Comet server used for streaming real-time data to web browsers and other clients. This tutorial assumes you know what Comet is and have some basic JavaScript and Java knowledge but it doesn't get too deep so don't worry. At the end of the tutorial we will have created a simple cross-browser chat application.

Step 1 - Download the Free Community Edition

First of all we need to download the StreamHub SDK from the website. Go to the StreamHub download page and grab yourself a copy of the Community Edition. At time of writing the latest version was 2.0.1 but any version should work. There isn't any real difference between the .ZIP version and the .TAR.GZ version - so grab which ever one you prefer. After downloading, extract the archive somewhere you can store it permanently. I put mine in C:\streamhub. Don't worry at this point if you're using Linux or Mac - the StreamHub server is completely cross platform.

Step 2 - Creating the chat page

We'll need a webpage to host the chat application so let's create that first. Create a new folder in your StreamHub directory called Chat, so now you'll have something like C:\streamhub\Chat. Create a new file in the Chat directory called index.html. This will be our Comet chat page. Edit that file, adding the following:
<html>
<head>
<title>Comet Chat with StreamHub</title>
</head>
<body>
<input type="text" id="chatText" onkeypress="checkForEnter(event)">
<input type="button" value="Chat" onclick="chat()">

<div id="chatMessages"></div>
<script>

function chat() {
// ...
}

function checkForEnter(e){
var e = e || event;
var key = e.keyCode || e.charCode;

if(key == 13){
chat();
}

return true;
}
</script>
</body>
</html>
All we've done is to create a very simple page with a textbox and a button labelled 'Chat'. We've added an onclick handler and an onkeypress handler. Don't worry too much about the checkForEnter() function, this just handles the user pressing enter in the textbox so the user can chat by pressing enter or clicking 'Chat'. The chatMessages div will hold any chat messages that we receive. We'll need to include the StreamHub JavaScript API, so lets do that now. The JavaScript API comes as a single mini-fied file called streamhub-min.js. Its in the archive we extracted in step 1, mine was in the directory C:\streamhub\streamhub-java-adapter-sdk-1.0\examples\lib\js. Copy this file and paste it in the Chat directory. We'll include it in the head of our chat page, so it should now look like:
<html>
<head>
<title>Comet Chat with StreamHub</title>
<script src="streamhub-min.js"></script>
</head>
<body>
<input type="text" id="chatText" onkeypress="checkForEnter(event)">
<input type="button" value="Chat" onclick="chat()">

<div id="chatMessages"></div>
<script>

function chat() {
// ...
}

function checkForEnter(e){
var e = e || event;
var key = e.keyCode || e.charCode;

if(key == 13){
chat();
}

return true;
}
</script>
</body>
</html>

Step 3 - Creating a Java Chat Adapter

Now we have a basic webpage setup we'll need to write some code in the backend to handle the receiving of messages and sending of messages to all connected chat clients. We'll create a Chat Java class to do this.

Create a new file in the chat directory called Chat.java, and add the following code to it:
import java.io.File;
import com.streamhub.api.PushServer;
import com.streamhub.nio.NIOServer;
import com.streamhub.api.Client;
import com.streamhub.api.PublishListener;
import com.streamhub.api.JsonPayload;
import com.streamhub.api.Payload;

public class Chat implements PublishListener {
private PushServer server;

public static void main(String[] args) throws Exception {
new Chat();
}

public Chat() throws Exception {
server = new NIOServer(7878);
server.addStaticContent(new File("."));
server.start();
server.getSubscriptionManager().addPublishListener(this);
System.out.println("Comet server started at http://localhost:7878/.");
System.out.println("Press any key to stop...");
System.in.read();
server.stop();
}

public void onMessageReceived(Client client, String topic, Payload payload) {
Payload chatMessage = new JsonPayload(topic);
chatMessage.addField("client", "Client-" + client.getUid());
chatMessage.addField("message", payload.getFields().get("message"));
server.publish(topic, chatMessage);
}
}
In the Chat class we've created a new Comet server and started it on port 7878:
server = new NIOServer(7878);
server.start();
We've then added our Chat class as a PublishListener:
server.getSubscriptionManager().addPublishListener(this);
This means that every time a client publishes something to the server we will get notified by a call to the onMessageReceived method. Later, in our chat webpage, we will publish every chat message to the server. So each chat message will come through to the onMessageReceived method. In this method we simply publish the chat message to every single user subscribed to the chat:
public void onMessageReceived(Client client, String topic, Payload payload) {
Payload chatMessage = new JsonPayload(topic);
chatMessage.addField("client", "Client-" + client.getUid());
chatMessage.addField("message", payload.getFields().get("message"));
server.publish(topic, chatMessage);
}
The client is an object representing the user who sent the chat message. The topic is the name of the topic they published on (we'll see later this is just "chat"). The payload is the JSON message that was published by the user. We will put each chat message in the field "message". We add two fields to the chatMessage payload: "client" which shows which user sent the chat message and "message" which shows the actual chat message. A UID is a unique identifier for each client, it gives each user a name, so we can see who said what. In a richer chat application would would probably add a way of having users choose there own name, but this will do for our simple example.

Step 4 - Connecting to the Server and Publishing Chat Messages

Now we have a Chat adapter, we need to add the code to our chat webpage to connect to the server and send the chat messages.

The code to connect looks like this:
var hub = new StreamHub();
hub.connect("http://localhost:7878/");
Now we are connected, we can subscribe to chat updates. We do this by passing a topic and a function which will be called every time a new chat message is received:
hub.subscribe("chat", chatUpdated);
We, define the chatUpdated() function elsewhere. Every data updated callback will receive two arguments: sTopic, the topic on which the update occurred and oData, a JSON object containing the fields which were updated. In the chatUpdated() function we extract the client who sent the chat message and the message itself and append it to the chatMessages div:
function chatUpdated(topic, data) {
var div = document.createElement("DIV");
div.innerHTML = data.client + ": " + data.message;
document.getElementById('chatMessages').appendChild(div);
}
Our chat webpage should now look like this:
<html>
<head>
<title>Comet Chat with StreamHub</title>
<script src="streamhub-min.js"></script>
</head>
<body>
<input type="text" id="chatText" onkeypress="checkForEnter(event)">
<input type="button" value="Chat" onclick="chat()">

<div id="chatMessages"></div>
<script>

function chatUpdated(topic, data) {
var div = document.createElement("DIV");
div.innerHTML = data.client + ": " + data.message;
document.getElementById('chatMessages').appendChild(div);
}

function chat() {
// ...
}

function checkForEnter(e){
var e = e || event;
var key = e.keyCode || e.charCode;

if(key == 13){
chat();
}

return true;
}

var hub = new StreamHub();
hub.connect("http://localhost:7878/");
hub.subscribe("chat", chatUpdated);
</script>
</body>
</html>
As you can see, we still haven't filled in what happens when the user clicks "Chat". In the chat() function we will need to send the users chat message to the server so it can be forwarded to every user subscirbed to the chat. We do this by using the publish method. The first argument to publish is the topic we want to publish on. The second argument is a string of arbitrary JSON. Whatever we pass to the publish method will be passed through to the onMessageReceived function in our Chat Java class. So we get the chat message out of the textbox and create a JSON object containing one field, the message:
function chat() {
var message = document.getElementById('chatText').value;
var json = "{'message':'" + escapeQuotes(message) + "'}";
hub.publish("chat", json);
}
Notice we call an escapeQuotes() method on the input from the textbox before we add it to the JSON. We do this because if the user happened to type a single quote it would mess up our JSON since each field is delimited by a single quote. Don't worry too much about the innards of the escapeQuotes() method - it just takes care of escaping any single quotes that would mess up the JSON. Now we're almost ready to try it out - the entire chat webpage should now look like this:
<html>
<head>
<title>Comet Chat with StreamHub</title>
<script src="streamhub-min.js"></script>
</head>
<body>
<input type="text" id="chatText" onkeypress="checkForEnter(event)">
<input type="button" value="Chat" onclick="chat()">

<div id="chatMessages"></div>
<script>

function chatUpdated(topic, data) {
var div = document.createElement("DIV");
div.innerHTML = data.client + ": " + data.message;
document.getElementById('chatMessages').appendChild(div);
}

function chat() {
var message = document.getElementById('chatText').value;
var json = "{'message':'" + escapeQuotes(message) + "'}";
hub.publish("chat", json);
}

function escapeQuotes(sString) {
return sString.replace(/(\')/gi, "\\$1").replace(/(\\\\\')/gi, "\\'");
}

function checkForEnter(e){
var e = e || event;
var key = e.keyCode || e.charCode;

if(key == 13){
chat();
}

return true;
}

var hub = new StreamHub();
hub.connect("http://localhost:7878/");
hub.subscribe("chat", chatUpdated);
</script>
</body>
</html>

Step 5 - Compiling the Chat Adapter and Starting Up

In this final step, we will compile the Chat class we created in Step 3, start it up and try out the chat application. Before we compile and start we need to copy over a few files.

We'll need a license, we can use the Free Community Edition license which comes in the archive we downloaded in Step 1. Copy the license.txt file over to the Chat directory. Mine was at C:\streamhub\streamhub-java-adapter-sdk-1.0\license.txt.

We'll also need a few JARs to compile and run. All the JARs come in the archive we extracted in Step 1. Mine were in the folder C:\streamhub\streamhub-java-adapter-sdk-1.0\examples\lib\jar. Copy the following JARs over to the Chat directory:
  • streamhub-VERSION.jar (e.g. streamhub-2.0.2.jar)
  • log4j-1.2.14.jar
  • json-20080701.jar
Lastly, so we can see some logging, copy over the whole conf folder from the archive to the Chat directory. Mine was found here: C:\streamhub\streamhub-java-adapter-sdk-1.0\conf.

Now, to compile, open a Command Prompt in the Chat directory and issue the following command:
javac -cp streamhub-2.0.2.jar Chat.java
To run, issue the following command:
java -cp json-20080701.jar;log4j-1.2.14.jar;streamhub-2.0.2.jar;. Chat
If you are using Unix (or Mac), classpath items are separated with ':' instead of ';', so you will need to run the following command instead of the above:
java -cp json-20080701.jar:log4j-1.2.14.jar:streamhub-2.0.2.jar:. Chat
That's it, navigate to http://localhost:7878/, type some text and chat away! Open up a couple of browser windows and see the Comet chat messages displayed in both windows.

That's the tutorial finished. The source code is available to download here: CometChat-2.0.2.zip. A good next step is to explore the examples that come with the Community Edition. We hope you found this useful - if you have any problems or suggestions for other guides and tutorials please comment.

Tuesday, 14 July 2009

Getting Started with StreamHub and Comet

This article has now been superseded by the similar: Getting Started with Reverse Ajax and Comet.

This article refers to version 2.0.2. Download 2.0.2 here.

Introduction

This is a quick getting started guide for StreamHub. StreamHub is a lightweight Comet server used for streaming real-time data to web browsers and other clients. In this article I'll take you through how to create a simple 'Hello World' Comet application using the StreamHub Java SDK for Streaming and the AJAX Client API. By the end of the guide we'll have a cross-browser webpage that will be receiving real-time data pushed from the server. This guide assumes you know what Comet is and have some basic JavaScript and Java knowledge but it doesn't get too deep so don't worry.

Step 1 - Download the Free Community Edition

First of all we need to download the StreamHub SDK from the website. Go to the StreamHub download page and grab yourself a copy of the Community Edition. At time of writing the latest version was 2.0 but any version should work. There isn't any real difference between the .ZIP version and the .TAR.GZ version - so grab which ever one you prefer. After downloading, extract the archive somewhere you can store it permanently. I put mine in C:\streamhub. Don't worry at this point if you're using Linux or Mac - the StreamHub server is completely cross platform.

Step 2 - Creating the 'Hello World' web page

Let's create a simple web page to host our Comet application. Create a new folder in your StreamHub directory called HelloWorld, so now you'll have something like C:\streamhub\HelloWorld. Create a new file in the HelloWorld directory called index.html. This will be our Comet web page. Edit that file, adding the following:
<html>
<head>
<title>Hello World Comet Application</title>
</head>
<body>
<h1>Hello World Comet Application</h1>
<input type="button" value="Start streaming" onclick="startStreaming()">
<div id="streamingData"></div>
<script>
function startStreaming() {
// empty for now
}
</script>
</body>
</html>
That's it - all we've done so far is create a very basic HTML page with a single button that when clicked triggers an empty JavaScript function. We'll need to add the StreamHub JavaScript API in order to connect to the server, so lets grab that from the Community Edition we downloaded earlier and include it in our script. The JavaScript API comes as a single mini-fied file called streamhub-min.js. Its in the archive we extracted in step 1, mine was in the directory C:\streamhub\streamhub-java-adapter-sdk-1.0\examples\lib\js.

Copy this file and paste it in the HelloWorld folder. Now we just need to include it in our web page, the new index.html should look something like this:
<html>
<head>
<title>Hello World Comet Application</title>
<script src="streamhub-min.js" type="text/javascript"></script>
</head>
<body>
<h1>Hello World Comet Application</h1>
<input type="button" value="Start streaming" onclick="startStreaming()">
<div id="streamingData"></div>
<script>
function startStreaming() {
// empty for now
}
</script>
</body>
</html>
Step 3 - Creating a Java Adapter for Streaming

Now we've got a basic webpage we need to provide some sort of data to it before we connect to the Comet server. So in this section we'll be creating a HelloWorldStreamer Java class to stream data to our webpage.

Create a new file in the HelloWorld directory called HelloWorldStreamer.java. First we need to add the code to create and start the Comet server. Edit HelloWorldStreamer.java, adding the following:
import com.streamhub.api.PushServer;
import com.streamhub.nio.NIOServer;

public class HelloWorldStreamer {
public static void main(String[] args) throws Exception {
new HelloWorldStreamer();
}

public HelloWorldStreamer() throws Exception {
// Create a new server on port 7878
PushServer server = new NIOServer(7878);
server.start();
System.out.println("Comet server started at http://localhost:7878/.");
System.out.println("Press any key to stop...");
System.in.read();
server.stop();
}
}
This creates the Comet server and starts it. To actually stream some data to our webpage we'll need to add ourselves as a listener by changing the code to:
import com.streamhub.api.Client;
import com.streamhub.api.SubscriptionListener;
import com.streamhub.api.PushServer;
import com.streamhub.nio.NIOServer;

public class HelloWorldStreamer implements SubscriptionListener {
public static void main(String[] args) throws Exception {
new HelloWorldStreamer();
}

public HelloWorldStreamer() throws Exception {
// Create a new server on port 7878
PushServer server = new NIOServer(7878);
server.start();
server.getSubscriptionManager().addSubscriptionListener(this);
System.out.println("Comet server started at http://localhost:7878/.");
System.out.println("Press any key to stop...");
System.in.read();
server.stop();
}

public void onSubscribe(String topic, Client client) {

}

public void onUnSubscribe(String topic, Client client) {

}
}
Notice we now implement SubscriptionListener which requires us to implement the onSubscribe and onUnSubscribe methods. These methods will be called when a client, such as our HelloWorld web page, subscribes or unsubscribes to a particular topic. For now lets just send a synchronous response back to the client when they subscribe:
import com.streamhub.api.Client;
import com.streamhub.api.SubscriptionListener;
import com.streamhub.api.PushServer;
import com.streamhub.nio.NIOServer;
import com.streamhub.api.JsonPayload;

public class HelloWorldStreamer implements SubscriptionListener {
public static void main(String[] args) throws Exception {
new HelloWorldStreamer();
}

public HelloWorldStreamer() throws Exception {
// Create a new server on port 7878
PushServer server = new NIOServer(7878);
server.start();
server.getSubscriptionManager().addSubscriptionListener(this);
System.out.println("Comet server started at http://localhost:7878/.");
System.out.println("Press any key to stop...");
System.in.read();
server.stop();
}

public void onSubscribe(String topic, Client client) {
JsonPayload payload = new JsonPayload(topic);
payload.addField("Response", "Hello World!");
client.send(topic, payload);
}

public void onUnSubscribe(String topic, Client client) {

}
}
All we do here is create a new Payload (or message), add a field with a key of "Response" and value of "Hello World!", then send it to the client that subscribed. We'll see how to subscribe in the next step.

Step 4 - Connecting to the Comet Server and Subscribing to a Topic

Go back and edit the index.html file in the HelloWorld directory. We're going to add the code to connect and subscribe:
<html>
<head>
<title>Hello World Comet Application</title>
<script src="streamhub-min.js" type="text/javascript"></script>
</head>
<body>
<h1>Hello World Comet Application</h1>
<input type="button" value="Start streaming" onclick="startStreaming()">
<div id="streamingData"></div>
<script>
function topicUpdated(sTopic, oData) {
var newDiv = document.createElement("DIV");
newDiv.innerHTML = "Update for topic '" + sTopic + "' Response: '" + oData.Response + "'";
document.getElementById('streamingData').appendChild(newDiv);
}

function startStreaming() {
var hub = new StreamHub();
hub.connect("http://localhost:7878/");
hub.subscribe("HelloWorld", topicUpdated);
}
</script>
</body>
</html>

Connecting is really easy. We just create a new StreamHub object and connect to the Comet server we created in HelloWorldStreamer.java:
    var hub = new StreamHub();
hub.connect("http://localhost:7878/");
To subscribe we need to specify a topic and an update listener.
    hub.subscribe("HelloWorld", topicUpdated);
The topic will be passed to the onSubscribe method in our HelloWorldStreamer. The update listener is just a JavaScript function with the arguments sTopic, the topic that has been updated, and oData, a JSON object containing the Payload we sent in our onSubscribe method. You don't need to understand too much about JSON for this guide, just know that any field we put in the Payload in our HelloWorldStreamer, we can get that same field out of oData using the key. In our listener we get the 'Response' field put it inside a new div and append it to the streamingData div:
function topicUpdated(sTopic, oData) {
var newDiv = document.createElement("DIV");
newDiv.innerHTML = "Update for topic '" + sTopic + "' Response: '" + oData.Response + "'";
document.getElmentById('streamingData').appendChild(newDiv);
}
If you don't understand too much about JavaScript - this will just display every message we receive on our webpage inside the streamingData HTML element. Note: update listeners don't need to have a specific name, we've just opted to call this one topicUpdated. OK, now we're almost ready to try it out.

Step 5 - Compiling HelloWorldStreamer and Starting the Server

Just before we compile HelloWorldStreamer we need to make a couple of changes. Using addStaticContentDir we will make the HelloWorld folder available via the Comet server. Setting the file to "." means we're adding the current directory. Although StreamHub is capable of serving normal static content, for example HTML pages, it was primarily designed as a Comet server. For a live website I would recommend serving static content from a standard HTTP Web server such as Apache or IIS. However, for our 'Hello World' application this will do just fine.

To compile and run HelloWorldStreamer we just need to copy a few Java JAR libraries and the license file. The JARs can be found in the Community Edition downloaded in step one. You will need streamhub-2.0.jar, json-20080701.jar and log4j-1.2.14.jar. Mine were in the folder C:\streamhub\streamhub-java-adapter-sdk-1.0\examples\lib\jar. Copy the JARs to the HelloWorld directory. You will also need a license file, if you have an evaluation or development license copy this over to the HelloWorld directory, otherwise copy the Community Edition license over. I used the Community Edition license at C:\streamhub\streamhub-java-adapter-sdk-1.0\license.txt. You also might want to copy over the conf directory containing the log4j configuration. Now, open a command prompt, navigate to the HelloWorld directory and execute the following command to compile:
javac -cp streamhub-2.0.2.jar HelloWorldStreamer.java
Now start it up:
java -cp .;streamhub-2.0.2.jar;json-20080701.jar;log4j-1.2.14.jar HelloWorldStreamer
On Unix systems you'll need to replace the ';' with a ':', like so:
java -cp .:streamhub-2.0.2.jar:json-20080701.jar:log4j-1.2.14.jar HelloWorldStreamer
You should now see a message along the lines of:
Comet server started at http://localhost:7878/.
Press any key to stop...
Open a browser and go to the address http://localhost:7878/. You should now see the HelloWorld web page we created earlier. Click the "Start streaming" button. You should now see a message on the page saying:
Update for topic 'HelloWorld'. Response: 'Hello World!'
That's it - your first Comet message! However, one message is pretty boring, let's change the HelloWorldStreamer to continually stream messages in true Comet fashion.

Step 6 - Updating HelloWorldStreamer to Continuously Stream

Stop the server if it's still running and open HelloWorldStreamer.java for editing. First of all we're going to add a new ContinuousStreamer inner class which streams data until you stop the server. The final HelloWorldStreamer.java should look like this:
import java.io.File;
import com.streamhub.api.PushServer;
import com.streamhub.nio.NIOServer;
import com.streamhub.api.Client;
import com.streamhub.api.SubscriptionListener;
import com.streamhub.api.JsonPayload;

public class HelloWorldStreamer implements SubscriptionListener {
public static void main(String[] args) throws Exception {
new HelloWorldStreamer();
}

public HelloWorldStreamer() throws Exception {
PushServer server = new NIOServer(7878);
server.addStaticContent(new File("."));
server.start();
server.getSubscriptionManager().addSubscriptionListener(this);
ContinuousStreamer continuousStreamer = new ContinuousStreamer(server);
new Thread(continuousStreamer).start();
System.out.println("Comet server started at http://localhost:7878/.");
System.out.println("Press any key to stop...");
System.in.read();
continuousStreamer.stop();
server.stop();
}

public void onSubscribe(String topic, Client client) {
JsonPayload payload = new JsonPayload(topic);
payload.addField("Response", "Hello World! Initial Response");
client.send(topic, payload);

}

public void onUnSubscribe(String topic, Client client) {

}

private static class ContinuousStreamer implements Runnable {
private boolean isStopped = false;
private int updateNumber = 0;
private final PushServer server;

public ContinuousStreamer(PushServer server) {
this.server = server;
}

public void run() {
while (!isStopped) {
JsonPayload payload = new JsonPayload("HelloWorld");
payload.addField("Response", "Hello World! Response number " + updateNumber++);
server.publish("HelloWorld", payload);

try {
// Sleep for one second (1000ms)
Thread.sleep(1000);
} catch (InterruptedException e) {}
}
}

public void stop() {
isStopped = true;
}
}
}
Notice in the ContinuousUpdateStreamer we don't use client.send(), but instead we use server.publish(). Using client.send() is a way of sending something to an individual client - whatever you send using this method will go to this client and this client only. Whereas, using server.publish(topic, payload) will send that payload to every client who is subscribed to that topic. In most cases you should use server.publish() as it knows best who is subscribed and who isn't. Using client.send() to send an initial message inside onSubscribe is a common and good use. The only other time to prefer client.send() is when each client will be receiving unique data. Lets compile and run one more time. Now when you click the "Start streaming" button you should see something like the following:
Update for topic 'HelloWorld' Response: 'Hello World! Initial Response'
Update for topic 'HelloWorld' Response: 'Hello World! Response number 8'
Update for topic 'HelloWorld' Response: 'Hello World! Response number 9'
Update for topic 'HelloWorld' Response: 'Hello World! Response number 10'
...

The updates will keep streaming until you stop the server. That's the guide finished. The source code is available to download here: CometHelloWorld-2.0.2.zip. A good next step is to explore the examples that come with the Community Edition. We hope you found this useful - if you have any problems or suggestions for other guides and tutorials please comment.