Ted received his PhD in mathematics in 1996, answering open problems in complexity theory and infinite colorings for ordered sets, and proceeded with post-doctoral research in component and Web-based collaborative technologies. Following work at Java Software, Sun Microsystems, he was a device management and XML architect at Wind River, participating in the IETF NETCONF design team. Ted currently participates in the JavaServer Faces and Servlet expert groups and is a senior software architect at ICEsoft Technologies developing ICEfaces, an Ajax framework for JavaServer Faces. Ted is a DZone MVB and is not an employee of DZone and has posted 8 posts at DZone. You can read more from them at their website. View Full User Profile

Asynchronous Ajax for Revolutionary Web Applications

11.11.2008
| 15910 views |
  • submit to reddit
What if a small change in the way that we use the Web could allow us to turn every application into a new communication tool? What if users could communicate with each other directly through our applications and more timely information was available, allowing people to make better business decisions. Hey, isn't Ajax already "asynchronous"? Not quite; we need Ajax Push.

To understand Ajax Push (variously called "Comet" or "Reverse Ajax") it is necessary to cover a variety of topics. After setting the stage with a practical definition of Web 2.0, we will proceed with an explanation of Ajax Push on the wire, move along to see the impact on the server and what server APIs to use, and then conclude with techniques for application development.

By Ted Goddard & Jean-Francois Arcand

Web 2.0 and the Asynchronous Revolution

Does "Web 2.0" have a useful interpretation that will allow us to build better applications, or is it just an O'Reilly trademark? To extract a useful definition, we need to look at some applications that are typically considered to fall into the category of "Web 2.0", applications such as eBay, Blogger, YouTube, and Flickr. There are a number of common characteristics to these applications, but what we want to focus on here is the fact that they are "created" by their users.

Now let's look more closely at eBay. Is eBay interesting because eBay has a bunch of things to sell us, or is eBay interesting because its users are posting items for auction and bidding on them? Clearly, the eBay application is built collaboratively by its users. eBay is a multi-user collaboration system, where the eBay application is created through the participation of its users. As Jonathan Schwartz said at JavaOne in 2005, we're moving out of the "Information Age" and into the "Participation Age". His intent was that software is now being developed collaboratively through open source, but the comment applies equally well to the web overall.

No longer is the web just about some institution putting their documents on a public file server and letting other people retrieve them. Now, the web is all about users building applications together with their contributions. We're posting auctions. We're posting Wikipedia articles. We're making entries on blogs, contributing photos and movies. All of these things contributed by users are building the web. In other words, the web as a whole is turning into a collaborative environment.

But if we drill down into an individual eBay page, where we're looking at an interesting auction item, we really have little more than a print-out on the screen. Other users could be bidding on the item, but we have no knowledge of that until we press "refresh". The web page itself is not collaborative or multi-user.

Ajax helps to improve the user experience, but it still doesn't allow us to see what the other users are doing at that time; the network operations are still synchronized to the user event stream. To build a collaborative application, we need the full realization of Ajax, or "Ajax Push" where we have an asynchronous channel from the server to the browser that allows us to update the page at any time.

How can we use such an asynchronous communication channel to build a more interesting application that allows users to collaborate?

Imagine that we have two users of a slideshow application: the moderator, Yoda, and a viewer, Luke. Yoda presses a button to advance the slide, thereby sending a user event to the server. The server processes this user event and, using its push capability, pushes the slide change out to Luke on the other computer. In other words, the push capability allowed us to turn the web application into a new type of communication tool. In this case, the new form of communication is a multi-user slide-show.

Now the revolutionary nature of this should be clear; if we look back through history for world-changing technologies, we can see that many of them were inventions that allowed people to communicate with each other in different ways: moveable type, radio, television, just to name a few. If we have a way to turn every web application into a new, unique, communication tool, we are standing on the brink of another revolution. If you take a step back and think about the application that you're working on today, you will realize that there are some very interesting ways to add push capabilities and multi-user interaction.

For instance, in the future, we'll see chat, forums, and blogging all merge into a single, hybrid application. Consider providing a blog with responses updated in real time. This would turn the blogging system into a chat system, and it would improve people's ability to communicate with each other through it.

Let's see what we need to make the revolution a reality.

Ajax Push on the Wire

There are three common techniques for implementing a Push protocol on top of HTTP: polling, long polling, and streaming. Let's examine each in turn.

Polling is not a particularly good option because it always seems unresponsive: either you choose your polling interval to be short in an attempt to receive messages with a minimum delay, and you swamp the server with polling requests (which makes the application unresponsive), or you choose your polling interval to be long to reduce load on the server, and the application seems unresponsive because the messages are not delivered in a timely fashion. Fundamentally, polling is not the right choice for building an event-driven system. (To be fair, there are application cases with very long polling intervals that make it the right choice; for instance, updating a page containing a report on today's final weather statistics. These are not collaborative applications, however).

Long polling (or "blocking HTTP") makes use of a delayed response on the server to push a message at the desired time. The browser performs an HTTP request for a message, and the server simply delays until a message is available, whereupon the process repeats with the browser again making a request for a message. This effectively inverts the message flow of HTTP. Since a slow server cannot be prohibited by the HTTP specification, this technique works well through proxies and firewalls. The only drawback is the network overhead required by the message requests, but this can be mitigated by accumulating messages on the server and sending them in batches.

HTTP streaming is the most efficient at the TCP level, but unfortunately encounters some complications with proxies and the JavaScript API. The potential problem with an HTTP proxy is that (depending on configuration) it may choose to buffer the HTTP response until it is complete. In the case of streaming, the response isn't complete until the application has exited, resulting in no messages being delivered to the browser. The other problem is that JavaScript does not provide a read() API, so to read from a stream, it is necessary to collect the entire stream as a buffer in memory, essentially becoming a memory leak in the browser.

As a point of reference, ICEfaces uses the blocking HTTP technique. The operation can be described very concretely because messages in ICEfaces contain only page updates: When the browser loads an ICEfaces page from the server, it executes some JavaScript whose first operation is to request any page updates. If there are no page updates at that time, the server simply waits. When a page update is ready, for instance when a user has typed in a chat message, the server responds with the associated page changes. The browser then asks again, and the cycle continues.

Ajax Push on the Server

With our wire protocol in hand, we are ready to move to the server, but the two favored push techniques do not scale well on the current Servlet 2.5 API. Let's look at the problem more closely and explore some of the different server APIs that offer solutions.

With Servlet 2.5, holding onto an HTTP response means never emerging from the service() method, thereby consuming a thread. Since we need a push connection for each user, this causes us to consume a thread on the server for every single user. That's not generally good for scalability.

Instead what we need to do is handle multiple requests and responses with a small thread pool. Let's look at some of the various options for this available on Jetty, Tomcat, Resin, WebLogic, and GlassFish, and look slightly into the future with Servlet 3.0.

Jetty

Jetty allows us to suspend request processing with a Continuation. This is not a full Java language Continuation (analogous to making Thread Serializable) but is specific to request processing. By calling continuation.suspend() an exception will be thrown that will be caught by Jetty, causing the request/response pair to be associated with that continuation. Then later, say when it's time to update the page with a chat message, continuation.resume() will call the service() method once again with the same request and response. It's a bit unusual to re-enter the service method with the same objects, but the advantage is that the Servlet API requires no modification.


import org.mortbay.util.ajax.Continuation;

service(request, response) {
Continuation continuation = ContinuationSupport
.getContinuation(request, this);
...
continuation.suspend();
response.getWriter().write(message);
}

To cause the request processing to be resumed when a chat message is ready:

message.setValue("Howdy");
continuation.resume();

Tomcat

The Tomcat 6 API is perhaps closest to the NIO underpinnings of asynchronous request processing. The Tomcat CometProcessor interface must be implemented by the servlet handling the push interaction, but once in place, a variety of events are available, and the request and the response can be manipulated by an arbitrary Thread.

import org.apache.catalina.CometProcessor;

public class Processor implements CometProcessor {

public void event(CometEvent event) {
request = event.getHttpServletRequest();
response = event.getHttpServletResponse();

if (event.getEventType() == EventType.BEGIN) { ...
if (event.getEventType() == EventType.READ) { ...
if (event.getEventType() == EventType.END) { ...
if (event.getEventType() == EventType.ERROR) { ...
}

Later, when it's time to update the page with a chat message, event.close() causes the response to be complete and flushed to the browser.
message.setValue("Howdy");
response.getWriter().write(message);
event.close();

Resin

Resin splits the request handling into two methods that return a boolean indicating whether the request should be suspended.

public class CometServlet extends GenericCometServlet {
public boolean service(ServletRequest request,
ServletResponse response,
CometController cometController)
...
return true;
}

public boolean resume(ServletRequest request,
ServletResponse response,
CometController cometController)
PrintWriter out = res.getWriter();
out.write(message);
return false;
}
Later, when it's time to update the page with a chat message, cometController.wake() causes the resume() method to be called, and the response is flushed to the browser.
message.setValue("Howdy");
cometController.wake();

WebLogic

WebLogic is just like Resin, but the opposite. Returning false from doRequest() indicates that request processing should not continue.

import weblogic.servlet.http.AbstractAsyncServlet;
import weblogic.servlet.http.RequestResponseKey;

class Async extends AbstractAsyncServlet {

public boolean doRequest(RequestResponseKey rrk) {
... = rrk;
return false;
}

public void doResponse(RequestResponseKey rrk, Object message) {
rrk.getResponse().getWriter.write(message);
}
Later, when it's time to update the page with a chat message, AbstractAsyncServlet.notify() causes doResponse() to be called, and the response is flushed to the browser.
message.setValue("Howdy");
AbstractAsyncServlet.notify(rrk, message);

GlassFish

GlassFish provides the most flexible API and one of its strengths is that the asynchronous capability can be detected and used at runtime. When addCometHandler() is called on the cometContext, the request is suspended.

    CometContext context =
CometEngine.getEngine().register(contextPath);
context.setExpirationDelay(20 * 1000);
SuspendableHandler handler = new SuspendableHandler();
handler.attach(response);
cometContext.addCometHandler(handler);

class SuspendableHandler implements CometHandler {
public void onEvent(CometEvent event) {
response.getWriter().println(event.attachment());
cometContext.resumeCometHandler(this);
}
Later, when it's time to update the page with a chat message, cometContext.notify() can be used as an event mechanism to simplify application development, but the key method is cometContext.resumeCometHandler(), which causes the response to be flushed to the browser.
message.setValue("Howdy");
cometContext.notify(message);

Additionally, GlassFish provides delivery guarantee and Quality-of-Service APIs.

Atmosphere

 

Atmosphere (atmosphere.dev.java.net) is a framework that hides the container specific Comet/Ajax Push implementation, aiming to simplify the devlopment and improve the portablility of asynchronous web applications. Using the Atmosphere API, you can now write portable applications without worrying about the container you will deploy onto. More important, if a container doesn't support Comet/Ajax Push, Atmosphere will still work by emulating it directly within the framework.

Atmosphere aims to simplify the devlopment and improve the portablility of asynchronous web applications. Any Java object can be enhanced with asynchronous server features through a few annotations, as illustrated below:

@Grizzlet(Grizzlet.Scope.APPLICATION)
@Path("myGrizzlet")
public class MyGrizzlet{

@Suspend(6000)
@GET
@Push
public String onGet(){
return "Suspending the connection";
}

@POST
@Push
public String onPost(@Context HttpServletRequest req,
@Context HttpServletResponse res) throws IOException{
res.setStatus(200);
res.getWriter().println("OK, info pushed");
return req.getParameter("chatMessage");
}

Servlet 3.0

 

Servlet 3.0 will cover the ability to suspend and resume a connection, but will not support asynchronous read/write and delivery guarantee capabilities. As an integration of the best ideas found in existing APIs, the Servlet EG is always looking to the public for feedback. Should it have a re-entrant service() method? Should Filters be supported on both the inbound request and the outbound resumed response? Now is the time to voice your opinion.

 

The Two-Connection Limit

Having a great server API is only part of the story. A practical problem encountered when trying to implement push is the fact that most browsers generally have a limit of two connections to a given server. If the user opens two browser windows with each window attempting to maintain a push connection with the server, both of the permitted TCP connections are consumed. This means that any user events (such as clicking on a button or a link) will not be sent to the server -- there isn't an available TCP connection to deliver it over. Therefore, we need to share a single TCP connection across the multiple browser windows.

The ICEfaces Ajax Push Server was created to solve this by using JMS to coordinate all of the push updates over a single channel. In the browser, a JavaScript cookie-polling technique is used to share the notification of updates across the browser windows (HTML5 postMessage() would simplify the implementation here). This allows the reliable delivery of push updates from multiple web applications on the server into multiple browser tabs or windows.

For GlassFish, ICEfaces provides an update-center module that allows you to easily add the Ajax Push Server and install it, automatically configuring the JMS topics.

Developing Asynchronous Applications

We've looked at variations on the Servlet API that will allow us to develop scalable push applications, but the fact is that not many developers work directly with the Servlet API today. Developers work with framework APIs, such as Cometd, DWR, and ICEfaces, largely hiding the underlying Servlet capabilities. So, let's briefly examine how to use these frameworks.

For the developer concentrating on JavaScript, a great choice is Cometd. Cometd is based on the idea of publish/subscribe messaging mediated by a server-side reflector. Notice that the server is potentially just a reflector and could be implemented in any language, serving only as a message relay between JavaScript endpoints. Of course, server APIs are possible as well, and these are available for a variety of servers. Note that systems based purely on client message exchange suffer from the "late joiner problem". If you join the application late, you may not have the correct intermediate state to apply the current messages.

Moving more into the realm of the Java developer, we have DWR, or "Direct Web Remoting". DWR essentially provides the familiar Java RMI (or Remote Method Invocation, in case you don't find it familiar) bridged into JavaScript. Primarily, DWR is used to expose server-side JavaBeans through object-oriented JavaScript interfaces, but the "reverse" is also possible: DWR can be used to invoke JavaScript methods in the client from server-side Java code. There are two methods noteworthy for illustration: addScript() allows the application to invoke a common snippet of JavaScript on a group of specified pages, and setValue() allows the application to update a group of browser DOMs uniformly with a specified value. These are powerful client-side capabilities to have available on the server; however, they do require that the application retain detailed knowledge of the contents of the page being viewed.

Now that we've seen several APIs and frameworks, we should take a step back and ask what application developers and designers would ideally work with to produce an Ajax Push application.

First of all, developers should work mainly with JavaBeans, with a minimum of non-standard types (POJOs are preferred).

public class PageBean  {
String text;

public String getText() {
return text;
}

public void setText(String text) {
this.text = text;
}

}

Designers should work largely with HTML, but they will also need a natural expression language for dynamically binding to the JavaBean model.

<f:view
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html" >
<html>
<body>
<h:form>
<h:inputText value="#{pageBean.text}" />
</h:form>
</body>
</html>
</f:view>

But creating applications in such a natural way isn't only in our imagination. This clean separation between model and view is one of the great strengths of JavaServer Faces (JSF), and both developers and designers find it very productive (even if "develop" and "design" are roles taken by the same individual at different times). When we look at the syntax provided, we see that it says nothing about Ajax (no JavaScript is visible); yet, shouldn't it be sufficient to define an Ajax application? We have described the objects in the model (in Java), the components that are visible in the view (in XHTML), and how the view relates to the model (in expression language). A framework should be able to translate this into an Ajax application for us; indeed this is precisely the contract that ICEfaces provides to application developers. Nothing more is needed to define an Ajax application.

But what about push? When we consider that ICEfaces incrementally updates the page with current model values in response to user events, it's a small step to ask that the page be incrementally updated with current model values in response to server-side events. In other words, it's enough for the server-side application to be able invoke a render pass of a JSF page, provided the framework can push the incremental changes to the browser. This makes adding push features as simple as two lines of code.

For each user that belongs in a group named "chat":

SessionRenderer.addCurrentSession("chat")

We have added the current user's session into the group of sessions called "chat". Then, when a chat message is received and its time to push the update to the users' pages:

SessionRenderer.render("chat")

This renders all the windows of all the users in the "chat" group (automatically determining the incremental page changes), and pushes those page changes to the users' browsers. Adding push features to an existing JSF application can often be done in no more than two lines of code.

Additionally, more fine-grained APIs are available (such as for directing push updates to specific windows); please see the ICEfaces developer's guide for details.

Conclusion

It should now be clear that the asynchronous web is a revolutionary step forward for multi-user web applications, but such applications can be developed largely with current techniques. The key piece is Ajax Push, and it can scale to meet demand as long as the right application servers and APIs are applied carefully. Of course, the authors believe that you will find the combination of ICEfaces and GlassFish to be the most effective, allowing you to easily add multi-user features to your web application and allowing you to deploy it and scale to meet demand.

Published at DZone with permission of Ted Goddard, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Francis Walsh replied on Wed, 2008/11/12 - 9:15am

Here is what I have been thinking about. An interactive session where two or more people use a "Internet Buddy System (Tibbs.com)" to travel around enjoying the socialness of the internet and the information found within. Because humans are such a social creature (genetic footprint), this will become the "next step" for app builders who want to take social networks to the next level.

Jose Maria Arranz replied on Wed, 2008/11/12 - 9:45am

The scalability benefits of a NIO solution instead of a thread based is being debunked.

Anyway I agree, Comet is going to change how we develop/understand web applications, may be Comet is going to define the new Web 3.0 = Real Time Web.

By the way, add ItsNat to the list of Comet-capable Java web frameworks (the "add" word is rhetoric yes I know Ted is the technical leader of IceFaces).

 

Ted Goddard replied on Wed, 2008/11/12 - 12:12pm


Paul's presentation was very interesting, thanks for the reference.  Of course, threading, IO, and NIO performance are all very dependent on the particular JVM and operating system used. Historically, threads have been very expensive, but with new manufacturing methods they have come down dramatically in price. Similarly, we can expect to see optimizations for NIO (or AIO) that will allow it to lead in throughput as well as scalability.

Murali VP replied on Wed, 2008/11/12 - 7:49pm

Revolutionary? To me it sounds absurd. Defeats the purpose of HTTP or what it is meant for. So the servers "knows" all the clients' states and pushes data? Yikes! What a waste of time.

Jose Maria Arranz replied on Thu, 2008/11/13 - 2:49am in response to: Murali VP

This is the kind of opinion I've heard before when the "term" AJAX started to sound (and use). Past five years since today I invite you to review your opinion again.

By the way: HTTP was invented to serve static content, remind this to Google, Amazon or eBay :)

 

Murali VP replied on Thu, 2008/11/13 - 3:17am

That is not my point, the author seems to forget the fundamentals, can you imagine the kind of computing resources needed to scale and the complexity of a state machine to handle such a push technology? What I meant as purpose behind HTTP was the "stateless" nature of it (unlike FTP or Telnet). Can you imagine the the heartbeat messages and bandwidth of the server needed to keep the pipes open from a few clients to potentially millions, all concurrent? Either the author or I don't know what we are talking about, yes it has to be in such extremes.

Jose Maria Arranz replied on Thu, 2008/11/13 - 4:05am in response to: Murali VP

@muralive: That is not my point, the author seems to forget the fundamentals, can you imagine the kind of computing resources needed to scale and the complexity of a state machine to handle such a push technology?

Do you know the computing resources needed by Google, Amazon or eBay to serve millions of concurrent requests?

Do you know they are "real" applications handling this complexity? For instance this and this.

@muralive: What I meant as purpose behind HTTP was the "stateless" nature of it (unlike FTP or Telnet)

Yes, Tim Berners-Lee at CERN dixit many years ago, but HTTP + session cookie = stateful service, and HTTP + AJAX = no longer requesting complete pages again and again. We are in 2008.

@muralive: Can you imagine the the heartbeat messages and bandwidth of the server needed to keep the pipes open from a few clients to potentially millions, all concurrent?

In Comet, if nothing occurs nothing is sent, compulsive page reload by users is the real bandwidth consumer.

Do you know any average computer can handle thousand of threads and connections with no very much problem if they're most of the time in idle state? 

Does your web site handle millions of concurrent users? Yeah! you're Google!

 

Eirik Larsen replied on Thu, 2008/11/13 - 5:22am

Ted:

How does IceFaces ajax and comet technology relate to the upcoming ajax-standard for jsf2? Will IceFaces change it's implementation from jms-based to "jsf2"-based?

With jsf2, I'd expect most jsf-frameworks (richfaces, tomahawk) to start utilizing jsf2-ajax. Will IceFaces still be compatible with these frameworks, that is: can we use IceFaces ajax/comet to update components from for instance richfaces?

Thanks for an informative article.

Ted Goddard replied on Thu, 2008/11/13 - 11:22am in response to: Murali VP

[quote=muralive]That is not my point, the author seems to forget the fundamentals, can you imagine the kind of computing resources needed to scale and the complexity of a state machine to handle such a push technology?[/quote]

Jose is right; although the HTTP protocol itself is stateless, our usage today is not.  Increasing statefullness (whether the state is on the server or the client) is an indication of increasing sophistication. Push technologies allow us to do more interesting things on the web than we could do before.

Also, as Jose has pointed out, Ajax Push is not necessarily less efficient than Web 1.0 -- pushing a small delta to the page just when, say, an item price changes, is vastly more efficient than having all users frantically pressing refresh in the final moments of an auction.

Ted Goddard replied on Thu, 2008/11/13 - 11:37am in response to: Eirik Larsen

[quote=eirirlar]

How does IceFaces ajax and comet technology relate to the upcoming ajax-standard for jsf2? Will IceFaces change it's implementation from jms-based to "jsf2"-based?

With jsf2, I'd expect most jsf-frameworks (richfaces, tomahawk) to start utilizing jsf2-ajax. Will IceFaces still be compatible with these frameworks, that is: can we use IceFaces ajax/comet to update components from for instance richfaces?

Thanks for an informative article.

[/quote]

JavaServer Faces 2.0 will likely not include push capability; we're hoping to return to that in 2.1, (the first priority is Ajax standardization). 

One of the main pieces of ICEfaces (and the other JSF Ajax frameworks) is the mechanism for

  1.  serializing an HTML form in the browser
  2. sending it to the server via XMLHttpRequest
  3. executing the JSF Lifecycle and rendering the page, and
  4. updating the browser page from the HTTP response.

This complete stack is being standardized in JSF 2.0 (including a a JavaScript API and a standard format for encoding the HTML page updates) and would be factored out of ICEfaces. As you can imagine, when the various JSF frameworks make use of this standard stack, interoperability should be excellent.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.