Dojo DataGrid Test

I am looking at representing the simulated streaming data in a more friendly representation on the web. Dojo DataGrid seems to be a choice. Thus, this is my first test on Dojo DataGrid. For full description on Dojo DataGrid , refer to http://dojotoolkit.org/documentation/tutorials/1.7/datagrid/

<!DOCTYPE html>
<html>
<head>
<title>Linux System Activity Report</title>
<link rel="stylesheet" type="text/css"
 href="http://ajax.googleapis.com/ajax/libs/dojo/1.7/dijit/themes/claro/claro.css" />

<link rel="stylesheet" type="text/css"
 href="http://ajax.googleapis.com/ajax/libs/dojo/1.7/dojox/grid/resources/Grid.css" />

 <link rel="stylesheet" type="text/css"
 href="http://ajax.googleapis.com/ajax/libs/dojo/1.7/dojox/grid/resources/claroGrid.css" />
</head>

<body class="claro">
    <h1>Linux System Activity Report</h1>
    <div style="width: 840px; height: 200px">
        <table id="sar" dojoType="dojox.grid.DataGrid">
            <thead>
                <tr>
                    <th field="time">Time</th>
                    <th field="cpu">CPU</th>
                    <th field="user">%user</th>
                    <th field="nice">%nice</th>
                    <th field="system">%system</th>
                    <th field="iowait">%iowait</th>
                    <th field="steal">%steal</th>
                    <th field="idle">%idle</th>
                </tr>
            </thead>
        </table>
    </div>

<script type="text/javascript"
 src="http://ajax.googleapis.com/ajax/libs/dojo/1.5/dojo/dojo.xd.js"
    djConfig="parseOnLoad:true"></script>

<script type="text/javascript">
    dojo.require("dojox.grid.DataGrid");
    dojo.require("dojo.data.ItemFileReadStore");
</script>

<script type="text/javascript">
dojo.ready(function() {
    var sarvar = {
        items: [ {
                  "time":"10:30:16 AM",
                  "cpu":"all",
                  "user":"26.79",
                  "nice":"0.00",
                  "system":"7.66",
                  "iowait":"0.00",
                  "steal":"0.00",
                  "idle":"65.55"
                  },
                 {
                  "time":"10:30:17 AM",
                  "cpu":"all",
                  "user":"32.79",
                  "nice":"0.00",
                  "system":"8.99",
                  "iowait":"0.00",
                  "steal":"0.00",
                  "idle":"59.02"
                  },
                 {
                  "time":"10:30:18 AM",
                  "cpu":"all",
                  "user":"32.24",
                  "nice":"0.00",
                  "system":"6.78",
                  "iowait":"0.00",
                  "steal":"0.00",
                  "idle":"62.43"
                  },
            {
                  "time":"10:30:19 AM",
                  "cpu":"all",
                  "user":"32.79",
                  "nice":"0.00",
                  "system":"8.80",
                  "iowait":"0.00",
                  "steal":"0.00",
                  "idle":"63.94"
            }
               ],
        identifier: "time"
    };

    var dataStore = new dojo.data.ItemFileReadStore({ data:sarvar });
    var grid = dijit.byId("sar");
    grid.setStore(dataStore);
});
</script>

</body>
</html>

Deploy a CometD 2.x web application to Tomcat

I was writing a small system to simulate how the multiple data devices from the fields feeding data second by second to web server, and pass though to various clients (desktop, laptop, tablet, smart phones) in real time. The simulation is using data streaming through web servers by using Bayeux in CometD. It works great in jetty 7. However, when I deployed to Tomcat 7, I encountered the following error:

org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [cometd] in context with path [/DevServTest] threw exception
java.lang.IllegalStateException: Not supported.
    at org.apache.catalina.connector.Request.startAsync(Request.java:1664)
    at org.apache.catalina.connector.Request.startAsync(Request.java:1657)
    at org.apache.catalina.connector.RequestFacade.startAsync(RequestFacade.java:1022)
    at org.eclipse.jetty.continuation.Servlet3Continuation.suspend(Servlet3Continuation.java:171)
    at org.cometd.server.transport.LongPollingTransport.handle(LongPollingTransport.java:288)
    at org.cometd.server.CometdServlet.service(CometdServlet.java:181)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.eclipse.jetty.servlets.CrossOriginFilter.handle(CrossOriginFilter.java:212)
    at org.eclipse.jetty.servlets.CrossOriginFilter.doFilter(CrossOriginFilter.java:179)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:224)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:169)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:168)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:927)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:987)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:579)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:309)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
    at java.lang.Thread.run(Thread.java:636)

Although all the programs seem work still, the above error messages are annoying. How can I get rid of the messages?

In theory, CometD 2.x runs natively in Jetty 7.x and in Servlet 3 compliant servlet containers (including Tomcat 7.x) and only need some special configuration for Jetty 6.x and Tomcat 6.x.

In reality, you cannot just copy the war file for CometD 2.x and run from Tomcat 7.x. The challenge on how to embed Jetty 7.x into Tomcat is more on deciding which jars are needed.

Since most of Jetty based web applications are developed using mvn and tested through mvn jetty:run, the dependency are on pom.xml and the target war generated was supposed to include all the jars needed in WEF-INF/lib.

I found the walk-around by using Netbeans to import the mvn project and let Netbeans deploy directly to Tomcat 7. It works for me! Here is a Guide for Deployment Using Netbeans.

Comet / WebSocket: web server/browser support

Sometimes it is hard to keep track of which web server and browser are supporting Comet and/or WebSocket. The following is from JFarcand, the developer of Grizzly and Atmosphere framework.

Web Server Support

(LP: long-Polling HS: Http Streaming)

Server Version Native Comet Native WebSocket WebSockets LP HS JSONP
Netty 3.3.x X X X X X X
Jetty 5.x X X X
Jetty 6.x X X X X
Jetty 7.x X X X X X X
Jetty 8.x X X X X X X
GlassFish 2.x X X X X
GlassFish 3.x to 3.1.1 X X X X
GlassFish 3.1.2 X X X X X X
Tomcat 5.x X X X
Tomcat 6.x X X X X
Tomcat 7.0.26 and lower X X X X
Tomcat 7.0.27 and up X X X X X X
JBoss 5.x X X X
JBoss 6.x X X X X
JBoss 7.x X X X X
WebLogic 10.x X X X X
WebLogic 11.x and up X X X X
Resin 2.x X X X
Resin 3.x X  X X X X
WebSphere 7.x X X X
WebSphere 8.x X X X

Browser Support

Browser Version WebSockets Long-Polling Http Streaming JSONP
Firefox 3.x to 8.x X X X
Firefox 9.x to 11.x X X X X
Chrome 12.x and lower X X X
Chrome 13.x and higher X X X X
Internet Explorer 6x to 9.x X X X
Internet Explorer 10.x X X X X
Opera 10.x and lower X X
Opera 11.x X X X
Safari 4.x X X X
Safari 5.x X X X X
Android 2.x and up X X X
Safari (iOS) 1.x to 4.x X X X
Safari (iOS) 5.x X X X X

Browser Based Communication

History

The web has been largely built around the so-called request/response paradigm of HTTP. A client loads up a web page and then nothing happens until the user clicks onto the next page.

Around 2005, AJAX started to make the web feel more dynamic. Still, all HTTP communication was steered by the client, which required user interaction or periodic polling to load new data from the server.

Technologies that enable the server to send data to the client in the very moment when it knows that new data is available have been around for quite some time. They go by names such as “Push” or “Comet”. One of the most common hacks to create the illusion of a server initiated connection is called long polling. With long polling, the client opens an HTTP connection to the server which keeps it open until sending response. Whenever the server actually has new data it sends the response. Long polling and the other techniques work quite well. You use them every day in applications such as GMail chat.

Protocol: Bayeux vs WebSocket

The CometD framework is an implementation of the Bayeux protocol, which allows for a multi-channel asynchronous communication stream over unreliable networks between a client and server. Implementations of this are used in many languages (JavaScript, Java, Perl …) but predominantly browser-based AJAX applications. Bayeux has the advantage that it can run in any AJAX capable browser using nothing more than the underlying HTTP communication facilities provided by the browser to achieve asynchronous/background updates as new information comes in (like Google Mail’s new mail notifications). In fact, the same protocol can be used to connect devices in other languages and where network communication may be spotty, such as mobile devices.

WebSockets is a draft standard which is sponsored by Google, Apple and others at the WhatWG working group that is standardising HTML 5. As a result, HTML 5 capable browsers (Chrome, Safari) are starting to include built-in support for the WebSocket protocol.

Both protocols aim to allow web-based AJAX applications to communicate with other services via asynchronous messaging or socket-based connections, rather than having to roll your own communications layer on top of an existing application. This allows the design of an application to focus on the component parts, and hand off messages to the communication layer for delivery. In addition, both can set up long-running connections such that events can be delivered asynchronously subsequently to the application. This is nothing new: HTTP 1.1 supported connection pipelining (the ability to keep open a connection after each request, and the ability to send multiple requests before the first was acknowledged); and other protocols like IMAP supported the IDLE command to put a connection into a hibernate state, where there is no ongoing communication but the server can push new messages at any time. Indeed, prior to either Bayeux or WebSockets, the term “HTTP Push” was used to indicate a general mechanism for long-lasting communication channel across HTTP.

Problems and Challenges

The real challenge is HOW TO MAINTAIN THE CONNECTION!

A connection that hasn’t had any data over a period of time may be considered to have died, and arbitrarily terminated, at some point in the future. To address this, the IMAP IDLE suggests that clients send a re-negotiated IMAP IDLE command every 29 minutes to avoid disconnection. With other proxies in the way of HTTP, it may be that a proxy determines that a connection is idle and drops the connection, even if the client and server agree to maintain an ongoing connection.

The other problem is resource-based; generally, browsers limit the number of concurrent HTTP connections to a single server to avoid overbearing the server (or the network link). It’s common for browsers to limit such concurrent connections to between 2 and 4 at any one time.

Both Bayeux and WebSockets attempt to avoid the resource limits by using a fall-back mechanism for long polling (in the case of Bayeux) or switching to a non-HTTP based secondary protocol instead. As a result, users of these libraries don’t generally have to worry about limitations placed on them by the browser or infrastructure.

COMETD 2.x

CometD 2 now supports both Bayeux, and Websocket (which as an alternative transport to the currently supported JSON long polling and JSONP callback polling). CometD is transparent to browsers with or without websocket support. Websocket usage will be able to give us even better throughput and latency for cometd than the already impressive results achieved with long polling.

References

Bayeux Protocol Specification

W3C The WebSocket API

Alex Russell’s original 2006 comet blog post

Some test on WebSocket API

Websocket is a new protocol to enable the socket-based connection between the web client and server. It will maintain some persistent connection between web client and web server. It is based HTML5.

The only problem is not all the browsers and web servers are supporting HTML5. I am using firefox 11 and chrome 18.0.1025.168 to test. It works great.

The following is a test using HTML5 and websocket:

<!DOCTYPE html>  
<meta charset="utf-8" />  
<title>WebSocket Test</title>  
<script language="javascript" type="text/javascript">  
    var wsUri = "ws://echo.websocket.org/"; var output;  
    function init() { output = document.getElementById("output"); testWebSocket(); }  
    function testWebSocket() { 
        websocket = new WebSocket(wsUri); 
        websocket.onopen = function(evt) { onOpen(evt) }; 
        websocket.onclose = function(evt) { onClose(evt) }; 
        websocket.onmessage = function(evt) { onMessage(evt) }; 
        websocket.onerror = function(evt) { onError(evt) }; }  
    function onOpen(evt) { writeToScreen("CONNECTED"); doSend("WebSocket rocks"); }  
    function onClose(evt) { writeToScreen("DISCONNECTED"); }  
    function onMessage(evt) { 
        writeToScreen('<span style="color: blue;">RESPONSE: ' + evt.data+'</span>'); websocket.close(); }
    function onError(evt) { writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data); }  
    function doSend(message) { writeToScreen("SENT: " + message);  websocket.send(message); }  
    function writeToScreen(message) { 
        var pre = document.createElement("p"); 
        pre.style.wordWrap = "break-word"; 
        pre.innerHTML = message; output.appendChild(pre); }  
    window.addEventListener("load", init, false);  
</script>  
<h2>WebSocket Test</h2>  
<div id="output"></div>  
</html>