Asynchronous Update in Echo2

I've been sitting on my second in a series of Echo2 tutorials a little too long. (What can I say? Busy, busy.) The core of the tutorial is all about asynchronous updates -- updating the browser from the server as if by magic, without a click or a mouseover. There's a bit of a trick to doing this in Echo2 and understanding it depends on understanding how the framework handles asynchronous communication with the browser. (I apologize in advance that this isn't as verbose as a tutorial, but I thought it might be useful to someone in this form nonetheless.)

First you have to understand that this async update from the server isn't actually a server push but rather a client pull. On the client side, if asynchronous updates are turned on in an Echo2 app (more on how they are turned on later), then the browser polls the server (by default every .5 seconds) for updates. During updates, the Client Engine locks the UI, i.e. you can't click, drag or do anything while an update is taking place (so don't make your tasks long running). On the server side, async updates are turned on by creating a task queue. When the Client Engine contacts the server, those tasks in the queue are processed, one by one, and if any modifications are made to the UI model in on the server, those changes are propagated to the client. The reason for the queue is that Echo2 abides by the pre 1.3 J2EE spec that says that you cannot spawn threads that are not handled by the container. Therefore all modifications of UI components have to take place in a servlet thread. (In fact, if you try to modify any UI components from another thread, Echo2 will throw and exception.) The queue simply allows you to line up tasks that then are executed in the appropriate thread.

The act of creating such a queue causes the server to update the client so that the Client Engine periodically polls the server. If you destroy all of the queues, the client stops polling. (Of course a client-server communication has to take place in order for the client to be notified of this.)

But how do you put tasks in the queue? One naive approach would be to spawn a thread that periodically puts a task in the queue. The problem with this approach is that the queue is only cleared when the client polls the server. If you have a thread putting tasks in the queue and the client navigates away from your page, you could have a thread filling up the queue for quite some time. You also have to put lifecycle methods in place to clean up your thread and any associated resources. The cleaner solution is to override the hasQueuedTasks method of the ApplicationInstance class. This method is called every time an async poll comes in from the client.

Here is my simple solution to managing the async push in Echo2. Note that there are a few things missing here, such as clearing or pausing all scheduled tasks, catching exceptions, etc.. You'll have to decide some of those details on your own. Note that I've used a thread-safe set with a thread-safe iterator -- very handy:

package ajaxsample.app.echo2;

import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

import nextapp.echo2.app.ApplicationInstance;
import nextapp.echo2.app.TaskQueueHandle;
import nextapp.echo2.app.Window;
import nextapp.echo2.webcontainer.ContainerContext;

public class AjaxSampleApplication extends ApplicationInstance {

    private Window mainWindow;
    private TaskQueueHandle tqh;
    private final static int ASYNC_PERIOD = 5000; // 5 seconds
    private Set<UpdateTask> tasks;
   
    @Override
    public Window init() {
        tasks = new CopyOnWriteArraySet<UpdateTask>();
        tqh = createTaskQueue();
        // have the client poll every 5 seconds
        setUpdateInterval(tqh, ASYNC_PERIOD);
        mainWindow = new Window();
        mainWindow.setContent(new DefaultForm());
        mainWindow.setTitle("Ajax Sample");
        return mainWindow;
    }

    /**
     * Set the polling interval from the client for the task queue.
     *
     * @param tqh The task queue to modify
     * @param interval polling interval in milliseconds for client engine
     */
    private void setUpdateInterval(TaskQueueHandle tqh, int interval) {
        ContainerContext containerContext = (ContainerContext) getContextProperty(ContainerContext.CONTEXT_PROPERTY_NAME);
        containerContext.setTaskQueueCallbackInterval(tqh, (int) interval);
    }
   
    /**
     * Add an UpdateTask to the default taskQueue schedule
     * @param r
     */
    public void schedulePeriodic(UpdateTask r) {
        tasks.add(r);
    }

    /**
     * Remove a task from the default task schedule.
     *
     * @param r
     * @return was the task present in the default task schedule
     */
    public boolean unschedulePeriodic(UpdateTask r) {
        return tasks.remove(r);
    }
   
    /**
     * Insert tasks from out default schedule into the default queue.
     *
     * @return do we have queued tasks?
     * @see nextapp.echo2.app.ApplicationInstance#hasQueuedTasks()
     */
    @Override
    public boolean hasQueuedTasks() {

        Iterator<UpdateTask> it = (Iterator<UpdateTask>) tasks.iterator();
        while (it.hasNext()) {
            UpdateTask task = it.next();
            if (task.ready()) {
                enqueueTask(tqh, task);
            }
        }

        return super.hasQueuedTasks();
    }
   
   
    /**
     * Best practice. Returns the current application instance cast to the
     * correct type.
     *
     * @return the active <code>AjaxSampleApplication</code>
     */
    public static AjaxSampleApplication getApp() {
        return (AjaxSampleApplication) getActive();
    }

}

How would you make use of this queueing mechanism? Here's an example that updates a label every 5 seconds:

package ajaxsample.app.echo2;

public interface UpdateTask extends Runnable {
    boolean ready();
}

[...]

private static final long UPDATE_INTERVAL = 5000;
private Label countLabel = new Label();
private int count = 0;

[...]

// create an update task for the counter
UpdateTask task = new UpdateTask() {
    private long lastRun = 0; // in forever
    public boolean ready() {
        // more time has passed than our interval
        return UPDATE_INTERVAL <= (System.currentTimeMillis() - lastRun);
    }

    public void run() {
        // update the runtime
        lastRun = System.currentTimeMillis();
        count++;
        countLabel.setText("Counter = " + count);
    }
};

// now schedule it
AjaxSampleApplication.getApp().schedulePeriodic(task);

That's quite a bit of code that doesn't really fit the component GUI model. This is one area where I think ZK gets it right where Echo2 gets it wrong: the whole async update business should be abstracted into a timer component (see the timer in the ZK demo). If someone needs a special async update processing that cannot be accomodated through a timer, they can always hack the hasQueuedTasks method.

   

ZK - Documentation & Tools

Documentation

The quality of the ZK documentation is very high by Open Source standards. The documentation includes:

  • A 13 page PDF Quickstart guide - shows how to set up the demo application and a hello world app.
  • A 169 page PDF Developer's Guide - steps you through the ZK framework with lots of small examples illustrating components and concepts.
  • A 43 page PDF Developer's Reference. Documents component properties and behaviors. This document is only about 40% complete.
  • A 1 page PDF Executive Overview.
  • A 16 page PDF Product Overview - gives information on the motivations behind ZK and the architecture of the product.

The reference guide will need to be completed, otherwise developers will be guessing at specifics of components and the ZUML. Also, the documentation on CSS and "molds," i.e. the templates for components, is spotty. This will make it difficult for developers to change the look and feel in any significant way.

Tutorials

While there is a ZK demo application that shows off various components, there appear to be no tutorials or example/reference applications available to the would-be ZK developer. There is a wiki with a how-to/cookbook page, but just like the developer's guide, it consists of short examples, not a substantial application. There is also a port of a struts application over to ZK, but it seems to make use of so little of the AJAX capability of the framework that it can't really be considered a tutorial or reference app. For now the forums are probably the best source of information and support. Also, there is a "small talks" section of the project site that has some bits of information on integrating ZK with things like the Spring Framework.

Usability

Overall the framework is a joy to use. The ZUML makes it easy to do quick iterations -- edit, test, edit, etc. -- without a long compilation step. The ZUML is also easy enough so that non-programmers can compose or modify a UI. Exposing effects such as drag-and-drop, async update, etc., as components or attributes further eases the development of complex user interfaces for non-programmers and programmers alike.

Tools

As of this writing there is no IDE integration for ZK. This is really a crying shame, since the ZUML makes a WYSIWYG UI layout tool a natural fit.


MVC and RIA - Learning From Desktop Apps

Whether you're writing AJAX applications using enhancements to existing webapplication frameworks or adopting the new component GUI approach, you as a developer are faced with a new granularity of events. Where before you had the monster postback, now you must respond to mouse clicks, key presses, menu selections, etc.

On top of this, with a single page interface, you may have many more controls on the page. This brings on the problem of context, i.e. that not all of these controls are appropriate for a particular context such as when a particular part of a table or list box is selected or when the focus is on a particular window.

The above just scratches the surface of the sorts of challenges that you'll discover writing RIA's with AJAX technologies. The good news is that there are already solutions in place since these are the sorts of problems that desktop GUI developers face all of the time. For example:

  • Enabling and disabling menus and other controls can be done using the State pattern. A collection of state objects would be used to enable and disable the controls as appropriate.
  • Many of the component GUI frameworks implement an Observer pattern for event management. UI events come in now in little bites instead of one huge request and can be consumed by listeners that wait on a specific component.
  • MVC needs an upgrade when you're dealing with big heirarchies of components. Combining the Composite and Chain of Responsibility design patterns, you get the Heirarchical MVC (HMVC) pattern. (If you've worked with WebWork you may have run across the "Pull HMVC" pattern. That heirarchy refers to the model, not the whole MVC.)
  • The Command pattern is often used to implement undo.

That's just a sampling of desktop GUI solutions you can make use of. What can you do to expand your solution space for RIA's? Read lots of code. Find as many good open-source desktop GUI's as you can and read their code. You'll find ways of doing things that don't resemble the typical webapp approach. Here are a few to get you started.

Resources:

  • The Spring Rich-Client Project. Based on the Spring Framework. Used for buidling desktop GUI's. Read the code and get a sense for how to build your RIA framework. Good source for ideas
  • The Scope Framework. The bad new is that this is a desktop GUI framework based on Swing that hasn't been updated since 2002. The good news is that this is a great place to read some code and learn how to develop component GUI applications.
  • The HMVC Framework from Crionics. Again, no longer maintained, but a great place to read some code.
  • Tutorial (in C#) on implementing undo with the Command pattern.
  • A discussion of the Presentation-Abstraction-Control (PAC) architectural pattern. This one is just as old as the MVC pattern, but since the MVC pattern was intended for simple interfaces it was also simpler to implement. Thus MVC won out even in cases where PAC is more appropriate, such as with more complicated UI's.

Update 1: Oliver Steele has an excellent overview of MVC as it exists in the desktop, webapp and RIA worlds. He points out why non-RIA, i.e. traditional webapp programming is so tough:

That complexity includes the real world application of what are still research topics in academia: staged programming for multiple-target code generation (Mac IE, Windows IE, Netscape, Mozilla, Safari, Opera), process migration, and (usually manual) CPS conversion to maintain program state across pages.  No wonder web programming is hard!

Well worth a look.

Jetty 6's Continuation Mechanism for Ajax

I've touched on the topic of updates and asynchronous processing before. My preferred method of performing updates between the browser and server is via a polling mechanism that returns quickly. An alternative is to open up an XHR connection and keep it open to wait for a response (or a timeout). I don't like this method because it is wasteful in terms of sockets and threads; also, it is likely to stress stateful firewalls, load balancers, etc., and may break in lots of client environments.

Nevertheless, if you want to keep a connection open for notification initiated by the server, this is the way for now. And the Jetty 6 server has at least addressed the thread issue with Continuations.

Behind the scenes, Jetty has to be a bit sneaky to work around Java and the Servlet specification as there is no mechanism in Java to suspend a thread and then resume it later. The first time the request handler calls continuation.getEvent(timeoutMS) a RetryReqeuest runtime exception is thrown. This exception propogates out of all the request handling code and is caught by Jetty and handled specially. Instead of producing an error response, Jetty places the request on a timeout queue and returns the thread to the thread pool.

When the timeout expires, or if another thread calls continuation.resume(event) then the request is retried. This time, when continuation.getEvent(timeoutMS) is called, either the event is returned or null is returned to indicate a timeout. The request handler then produces a response as it normally would.

Sockets are still consumed, though. Hopefully the next servlet specification will address some of these issues. Until that time, this may be a good workaround.

Still, my preference is to keep everything except for the display logic on the server side, and that includes handling complex communication with async processing.

Update 1: ActiveMQ can make use of Jetty 6's continuation mechanism.

Update 2: Greg Wilkins has some more extensive thoughts on using Jetty 6 to scale Ajax apps.

Contact Us
ajax@pathf.com

Pathfinder Development Careers

Search


AgileAjax RSS Feed

AgileAjax Email Feed

  • email feed

    Enter your email address:

    Delivered by FeedBurner

Categories