The web has evolved from a document repository into a multi-user collaboration medium, shaped and created by its users. Ajax Push gives the server the ability to update any part of any page at any time, transforming every application into a new communication tool, connecting users to each other through web server mediated channels.
In this session, recorded at the recent JSFOne conference, Ted Goddard provides an overview of Ajax Push and its range of uses in multi-user web applications. By stepping through the development of a multi-user slideshow and chat system, he shows you how easily sophisticated Ajax applications can be created.
The complete transcript of the presentation has been provided below:
Hi. I'm Ted Goddard. Welcome to "Using ICEfaces for Ajax Push: Enterprise
We're going to talk about a few things here, introduce Web 2.0. What does it mean? Is it an O'Reilly trademark, or is it a useful word that we can make use of in understanding how to build applications? We'll talk about how to use Push for enterprise collaboration. Then we'll see some demos, two demos, showing multi-user Ajax capabilities.
Then, we'll look at how Ajax Push works on the wire. How does asynchronous HTTP work with browsers? What's the impact on the server? We'll take a look at that - it affects scalability. Then, we'll look at a bit of scopes and flows for Ajax. When you want to build a sophisticated Ajax Push application, you need to use more advanced scopes and flows than are available with the standard JSF Faces-config. Then, we'll drill a little deeper into how we developed some of those demos that I showed you.
Well, we'll pitch this as a revolution. This is a revolutionary change for the web. But, what kind of revolution am I signing you up for? Is it like the American Revolution, where you have to dump your favorite beverage into the bay, or maybe the French Revolution, where you have some great obstacle to overcome? Well, really, we'd like to keep our favorite beverage, Java, and we don't want some big learning curve.
Wouldn't it be better if this is like the Scientific Revolution, where a small change in perspective lets us do a lot more, see further, and use the tools and the knowledge that we have in a way that dramatically changes the kinds of applications that we can build.
Why, yes it is. But first, let's understand Web 2.0. In order to understand this beyond some marketing term, let's look at what we consider to be Web 2.0 applications and gather the common characteristics from them.
A good example is eBay. Is eBay interesting because eBay has a bunch of things to sell us, or is eBay interesting because the users of eBay are posting items for auction and other users are bidding on them? Really, the eBay application is built collaboratively by its users. So, it's a multi-user collaboration system, where the users are participating to create the eBay application. The eBay application is really a container for what the users are doing in it.
Jonathan Schwartz had a nice quote at JavaOne a few years back, where he said, "We're moving out of the Information Age into the Participation Age."
No longer is the web just about somebody having some documents, putting them on a file server, and letting other people retrieve them. Now, the web is all about users building the applications together, by 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 what are building the web. In other words, the web as a whole is turning into a collaborative environment.
But now, let's drill down into an individual eBay page, where you're looking at an item that you want to bid on. At that moment, it's almost like a print-out on your screen. You can't see what the other users are doing at that time. They could be bidding on the item, but you have no knowledge of that until you press "refresh." So, really, that page is still just a single-user page. It's not collaborative or multi-user.
Ajax helps a little bit to improve the user experience in this regard, but it doesn't really allow us to see what the other users are doing at that time. To do this, we need the full realization of Ajax, or Ajax Push. You may see this referred to as Comet, or Reverse Ajax, where we have an asynchronous channel, from the server to the browser, that allows us to update the page at any time.
Let's look at this basic idea in terms of enterprise collaboration. How can we use the server as a communication channel to mediate between users, in combination with push, to build a more interesting application?
So, imagine that we have two users. Maybe, they're in different countries. And they're using a slide-show application. Perhaps they're viewing this set of slides. One user is the moderator. Mika is the moderator, and he advances the slide. This sends a user event to the server. Then, the server processes that user event and, using its push capability, can push that slide change out to me on another computer.
So, by having this push capability as part of our framework, we're able to turn the server into a new type of communication channel. In this case, the communication that we're performing is a multi-user slide-show.
Well, perhaps our source of events into the server is not coming from users. It could be coming from a sensor network or an external application - some other software or hardware system. Those events can be propagated in, and pushed out to users as well.
So, there are two fundamental sources. And this is the key idea behind Ajax Push, that, by using the ability to send messages from the server to the browser, we can create new applications that are new forms of communication. As well, we can inform users, in a timely fashion, of events that are occurring in their enterprise or their environment.
If you take a step back and think about the application that you're working on today, there are probably some very interesting ways that you could add a push capability to that application.
Here are just some examples, on this slide, to give you some ideas of the categories. For instance, distance learning. You're trying to teach someone about a new concept. If you were able to build a web application that allowed you to push that information to them, you could do that more effectively.
Some of the interesting things that we're going to see in a demo today revolve around enterprise-shared locking and negotiation.
Then, of course, games are another interesting way to use push. As one user makes a move in the game, that game move can be pushed to the other users.
In the future, we'll see chat, forums, blogging, all of these things merge into a single application, a hybrid application. If you imagine that your blog, with its responses to your post, was updated as the various users in the system made posts as responses to your blog, you would be able to respond to them in a more timely fashion. And that would turn the blogging system, with comments, into a chat system.
Well, you may be wondering how you would actually implement applications like this. And the fact is that, using the ICEfaces framework, it's relatively easy, because it's based on the JavaServer Faces framework. And it allows you to build your application without violating any of the basic principles that JSF is based on.
For instance, one of the great successes of JSF is the clean separation between model and view. The model is implemented as JavaBeans, and the view is implemented declaratively through a markup language with expression language binding back to the beans.
If you consider what you should have to create in order to produce an Ajax application, really, we've already given a sufficient description to the system in order for it to build this application. We have told the system what the contents of the model are, and we've told the system what user-interface components to put into the page and how those components are related to the model. It should be the job of the framework to translate that for us into an Ajax user interface. After all, we're application developers, not Ajax developers.
ICEfaces does exactly that. It takes this description of the application, with no Ajax concerns in the page or in the model, and translates it into an Ajax user interface for us. It does so by providing a number of AJAX-based components.
What I would like to discuss here is, one of these components is not like the others. In fact, most of these components can be used in a purely synchronous fashion; they're based on user events. When I click on one of the components, an event is sent to the server, the page is updated, and the response is returned.
But one of these components really requires Push to function in any sensible way, and that's Progress Bar. Progress Bar is familiar to us on the desktop, but on the web, progress bars are somewhat difficult to implement because in order to show the accurate state of the progress on the server, we need some way to push those updates into the browser. Well, having Ajax Push mechanism is just what we need to implement that.
Now, let's move into some demos. First of all, we'll show a multi-user slideshow system that we discussed in that Ajax Push slide.
This is the WebMC application. As a moderator, I'll sign in and create a presentation named JSFOne. Then, I can upload my slides to make them available in the system, where they'll be unpacked and be ready for use in the presentation. Now, they're uploaded.
And now, as another user, I can sign into the JSFOne presentation and those slides are available to me there. When the moderator advances the slide to the next one, you can see that it's automatically updated in the other user's browser.
If you combine this with an audio channel, for instance, just a telephone or perhaps Skype, this will allow you to conduct a conference over the web. Ajax Push allows the moderator to move the slide presentation to the desired slide. It turns the web presentation application into a communication application.
There are other features in this application such as chat. We'll demonstrate that here.
In this case, the chat text is sent from the moderator to the server. The server pushes the update to the page to the other user's browser.
This application, WebMC, is completely open-source, freely available, you can download it from ICEfaces.org. You can also use the webMC.ICEfaces.org server to try it out or for any presentations that you want to share.
Now, back to the slides. Let's look at another demo. This is an interactive locking demo. This demo is developed jointly with our partner in Switzerland, mimacom.
Close these windows. Here, I'll log in as Mika. And here, I'll log in as Ted. And now, let's allow the two users to edit various records. For instance, I can select this record and click on "Edit". Perhaps I am changing one of the entries.
Both users are looking at the same dataset, and when I save it, it's pushed to the other users viewing that same page. So, both of these users are seeing a consistent, timely view of the same data model.
Well, perhaps I realize that there's something wrong with what I've done, and I want to edit that record again. And at the same time, the other user realizes that the record should be edited. If you go back to this one, we have acquired the lock on the left, on the right, we would like to edit that same record, but that record is locked. So, we can request it.
Our chat messages are pushed to the other user. So, the two users can discuss the state of this item that's locked. This allows them to negotiate, in human terms, what should be done with the record.
I can ask the other user to fix the record, I'll remove the window so that we can see the chat message as it appears. "Please fix it." All right, so that means that I'll cancel my edit and allow the other user to edit it. As now, the lock is released, they can change it and save. And we can see that the update is pushed to the other user. So, in this way, the users are working through a shared locking mechanism to edit a shared dataset.
Very common type of application, but now we've translated, or transformed, what was a problem of contention which perhaps we could've solved purely by some software means, but it would've meant a policy where only the last user record would be saved or something like that. In this case, by allowing the users to interact with each other, we can deal with contention in a more sophisticated way and actually allow people to resolve the problem and come to the correct answer.
What does this mean on the wire? How do we actually implement this Push technology in terms of a protocol? Well, there are really three options. Pulling is not a good option, because either you choose your pulling interval to be short, in which case you swamp the server with so many requests that the application seems slow, or you choose your pulling interval to be long, in which case, the time between one pull request and the next is so long that you don't receive timely updates.
What we use with ICEfaces is something called a long pull or blocking HTTP, where we make a request to the server and it responds precisely when it has an update available.
Another option for Ajax Push would be HTTP streaming, where the server just continually writes to a stream. In terms of the efficiency on the network, streaming might be the best choice.
The other problem is that an intermediate proxy may choose to buffer the HTTP response until it is complete. Well, in our case, the HTTP response wouldn't be complete until the application exited, and that's not the desired case at all.
But there are some complications. For instance, does this scale well?
If you consider that, with the Servlet API asking us to wait in the service method, before we issue a response, means that there will be a thread sitting there blocked in the service method. In other words, we would consume a thread on the server for every single user. That's not good for scalability.
So, what we would prefer to do is handle multiple requests and responses with a small thread pool. While not possible with the Servlet API currently, it is possible with additional APIs available on a variety of web servers. For instance, Jetty provides a continuation mechanism for this. Tomcat provides an event-based API that gives you suspend and resume. And GlassFish, as well, provides the Grizzly APIs for performing asynchronous reading, writing, suspend, and resume.
In addition, GlassFish provides quality of service that you can apply. The quality of service allows you to make it so that the system can degrade gracefully under load, so that, if you have more updates than the server can efficiently deliver at a particular time, you're able to prioritize those updates and deliver just the most important ones, meaning that the system behaves well even though it has exceeded its loaded capacity.
And Resin and WebLogic also provide similar APIs that allow us to suspend and resume requests and responses beyond what the Servlet API provides for us.
Well, now, work is ongoing in Servlet 3.0 to define a standard API so that we don't need to use these application-specific APIs. This will give us a standard API for asynchronous IO, potentially, and for suspending and resuming requests and responses. Since not that many people write Servlets nowadays, it may not affect your day-to-day code as writing an application, but it will affect the portability of various Ajax frameworks, such as ICEfaces, DWR, and Cometd.
For JSF 2.0, we would like to add this idea of push into JSF itself. And the idea here is to split the push concept into notification and refresh.
Notification could be delivered in a variety of ways. Perhaps it's just from a timer, or perhaps it's from a push mechanism as I described the protocol just now.
Refresh is simple to implement in ICEfaces. ICEfaces can render the DOM on the server at any time and determine just the updates and send them down to the browser. Other Ajax frameworks tend to be based on update regions, so they can't do this.
But, the notification could specify a target for the refresh. So, those targets could be reserved in the page, and this would allow just the desired target to be refreshed, which should be compatible with a variety of Ajax frameworks.
Now, another technical problem that we encounter when trying to implement push is the fact that the browser generally has a two-connection limit to the server. So, for a given host, the browser can only make two connections to that host. Well, you could imagine that if you open up two browser windows, each browser window having a push connection maintained to the server, you've now used up both of the TCP connections that are permitted.
What that means is that any user events that you attempt to generate - click on a button or a link - will not be sent to the server because there isn't an available TCP connection to deliver it over. So, what we need to do is to share a single TCP connection across the multiple windows in the browser.
Now, imagine that you have multiple web applications on the server, all of them using push. They're all in the same host, so there's only a single TCP connection allowed for the push updates from that single web application.
In the case of GlassFish, we've made an update-center module that allows you to easily add the Ajax Push Server and install it, automatically configure the JMS topics for you and the applications together, making it simple to deploy Ajax Push applications in this manner.
Let's move on to scopes and flows, because we're interested in building a sophisticated application with Ajax Push, but it turns out that request, session, and application scope are not sufficient for this.
Let's go back to the multi-window problem. Request scope lasts for each user event. So, it's a short-lived scope. Application scope is a much longer-lasting scope. It spends all of the user activity, all of the different user's activity, within the application. It's a very broad scope, and not easily used for storing user-specific data. In fact, you shouldn't store user-specific data in the application scope. Session scope lies in between these.
But now, we should observe that the multiple browser windows all share a common session scope. They're all in the same HTTP session. So, we don't have a formally defined scope, according to the Servlet specification, that allows us to distinguish multiple windows. We need something that lies between request and session.
Well, with ICEfaces, we've defined an extended request scope. What that does is it modifies request scope so that it lasts from one full-page refresh to the next. You could consider, when you open up multiple browser windows, each of these browser windows initiates a single full-page refresh, and then Ajax requests take place within each of those windows. That means that each of these distinct windows has a distinct extended request scope. It's actually a very effective way to distinguish your windows.
The downside of this is that it subverts request scope so that standard, short-lived request scope is then not available to your application. In most cases, that is an acceptable compromise because request scope is really rarely used.
But, there are cases where it is used. In fact, some of the cases where it's used most are when we integrate with third-party libraries, such as Seam or Spring Web Flow. But, the good thing about that is that Seam and Spring Web Flow both add new scopes of their own that go beyond request, session, and application, such as event, page, conversation, and business process in Seam and flash, flow, and conversation in Spring Web Flow.
We'll talk about each of these scopes in turn. So, the ICEfaces extended request scope is very useful for Ajax. It allows you to distinguish multiple windows. But really, we may be better off using one of the extended scopes in Seam or Spring Web Flow.
Seam has a request scope. It's actually called "event scope" in Seam. It's just the standard request scope.
Flash scope, in Spring Web Flow, allows you to carry objects from one request to the next. It's useful for propagating objects out of a user dialog.
Seam page scope is not the same as JSP page scope. It's actually backed by the ViewRoot. Now, this is a very useful scope for Ajax, because you'll have distinct ViewRoots for each of these distinct windows. This means that if you put things in Seam page scope, they'll be well-separated between the different windows. This is a good alternative from the ICEfaces' extended request scope.
Spring Web Flow flow scope is defined by the flow. And the flow is defined in your flow definition. But, multiple browser windows will have distinct flows. So, you can also use this as a replacement for ICEfaces' extended request scope.
Seam conversation scope is a sophisticated scope. It allows you to define a task. So, the task has a beginning and an ending; objects last for the duration of the task. It's a little more work to deal with than something like the view scope or the page scope, but as an application developer, you have more control over it, and it can actually span multiple window navigations. So, that's a good scope to use. You can use it with AJAX and Ajax Push.
The conversation scope in Spring Web Flow is a larger scope than the flow scope. It spans all of the flows within the conversation and is terminated when the flow reaches an end state. This is really like a super-flow state. You can use this as well, and replace the ICEfaces extended request scope. What you want to keep in mind is that it's defined by your web flow config.
Now, business process scope is available in Seam. This could be a very long-running scope indeed, and it's controlled by the business process manager.
Session scope, of course, you're familiar with this. The session is defined by the HTTP session. What you have to keep in mind is that objects in session scope are shared by all browser windows. So, if you put something like your selected index in the session scope, don't be surprised when that same record is selected in multiple windows. But still, session scope can be very useful even in AJAX applications because often you do want to share data between the different user windows.
Application scope is such a broad scope that in many ways it's difficult to use in applications for application-specific data. Often, people simply put configuration into application scope. One disadvantage is that application scope data is typically not propagated in clustering systems.
One scope that is particularly interesting is a new scope that'll be found in JSF 2.0, and this is explicitly called the view scope. So, in addition to requests, session, and application, in your faces-config, you can simply declare that an object should be stored with the view root. Well, that's very much like the Seam page scope and the ICEfaces extended request scope. It allows you to associate objects for a specific window that the user has open, effectively solving that problem.
If you don't need the added sophistication of defining objects that last for the duration of a task, but are simply available within a window, this is the scope that you need. And it will be very easy to use, you simply declare that the object is attached to the view.
Now, let's look in a little more detail at developing an asynchronous application. You've seen the WebMC demo. Now, let's look at how it's implemented.
We need an API to perform Ajax Push. The easiest API to use is the session renderer. With the session renderer, we have the idea of adding the current session to a group. This decision has to be made in an application-dependent manner.
For instance, when the user visits the chat area of the application, then you should add that user, that current session, to the chat group. Then later, pushing updates to all of those chat users is as simple as calling SessionRenderer.render("chat").
So, the way that you develop your application is you accept user input, you update the model to be the desired state, and then you call a render on the group that is relevant to that update. ICEfaces will render all of the pages in all of the sessions in that group, determine the changes to the page for you, and push those changes out to the browser. So really, developing a Push-based application is only two lines of code beyond a standard JSF application.
Just look at the typical code that we would find in WebMC application. It's a JSF application, so we import some namespaces. You can have markup. Organize your page into groups with layout components. Perhaps you have output text with static or dynamic values. And then, the key part of this application is the slide. Well, we don't need any AJAX concerns here, we simply need to say that the page has a graphic image with it whose value is derived from the current slide URL.
In our Bean, we have a getter for get slide URL. It's just recording the current state of the model. The moderator will change the value of the slide URL to the current slide.
We also have some methods for the chat log. Our input can be received and added to the log.
But now, let's look at the Ajax Push aspect. Import the session renderer. In the case of the WebMC application, we want to organize our users into groups of presentations. We created the JSFOne presentation, let's add the sessions of those users who are viewing the JSFOne presentation into the group associated with that presentation. So, we call add current session with the presentation name.
Then, when the moderator advances a slide, we simply call SessionRenderer.render("presentation name"). That will push out the changes to all of those users in that common presentation.
Now, we have an additional API that provides more fine-grained control. When I talked about the session renderer, I said that all the windows in all of the sessions in a given group will be rendered. But, what if you wanted to render just specific windows that the user had open? For scalability reasons, you might want to concern yourself with that, even thought it is a bit more bookkeeping in your application. In that case, you should use the persistent faces state object associated with that view.
Here, we call request render on the render manager. In this example, we're using an interval renderer, where we configure it with an interval. We add this renderable object to the render manager so that it can be rendered when the interval has elapsed.
In addition, we're able to observe exceptions and perform cleanup with these APIs. For instance, we might see exceptions if a user has closed their browser window and we attempt to push an update to it. That exception could be transient or fatal, depending on the conditions, either simply delayed heartbeat messages or an actual socket close that has taken place. Using the dispose bean interface, we can perform cleanup and we should remove ourselves from the clock render manager.
In order to configure this, you make use of the faces-config. Instantiate the render manager through the dependency injection mechanism in the faces-config and inject that render manager into the clock bean so that you can add yourself as a renderable to it.
In conclusion, I hope we've demonstrated that the asynchronous web is a revolutionary change in the type of web applications that we can make. And Ajax Push is the key for building collaborative enterprise applications. As you've seen, it can scale, as long as we use the right application servers and the right APIs, on those servers. And ICEfaces is what you need in order to add these features to your application, because ICEfaces provides the high-level application framework capabilities in order to develop Ajax Push.
Thank you and please contact us with any questions or comments that you might have.
About Ted Goddard