Multithreading and asynchronous updates/repaints with AjaxSwing

This section addresses deploying applications that use multiple threads of execution to perform business logic and user interface updates. When such an application is deployed using AjaxSwing, problems may surface where AjaxSwing will capture an intermediate state of a window and generate a page, and the final state of the window will not be shown. A typical example where this problem can occur is if in response to a button click the application displays a “Please wait…” dialog, starts a new thread to do the actual work and returns. When the newly started thread completes, it hides the Wait dialog and updates the window. If this application is deployed using AjaxSwing there is a chance that when the web users press a button on the HTML page what they will get in return will be a page with the Wait dialog, not the window that is updated or shown eventually.

The reason why this undesired behavior may happen is because AjaxSwing considers the action to be complete when the application returns from the event handling method, for example actionPerformed listener for a button or itemListener for a checkbox. AjaxSwing doesn’t know if the newly spawned threads represent a background process that is unrelated to the user interface, or if they actually perform the business logic and the action is not complete until they are finished. Note that using SwingUtilities.invokeLater and SwingUtilities.invokeNow should not be a problem, although it is not guaranteed.

While it may look like a serious problem, the solution is most of the times very easy and can be implemented quickly. There are several alternatives that can provide a solution to multithreading issues.

Programmatically controlling operations using AjaxSwingManager class

If the using application properties to synchronize AjaxSwing operations with the business logic does not provide sufficient control, one can opt for programmatic integration with AjaxSwing via AjaxSwingManager class. AjaxSwingManager defines a facade into the complex world of AjaxSwing's virtual runtime environment by exposing a number of static methods. The beauty of the design is that refering to AjaxSwingManager does not make your code dependent on presense of AjaxSwing because it will work with or without AjaxSwing. AjaxSwingManager has a function boolean isAjaxSwingRunning() which allows the application to query if it is running under AjaxSwing. To make matters even simpler, AjaxSwingManager gives two methods that allow the application programmer to directly demarcate the logic operation beginning and its end. The methods are

public static void beginOperation ()
public static void endOperation ()

As a developer or integrator, all you would have to do is call beginOperation() before you return from the event handling logic and call endOperation() at the end of the asynchronous processing, even if it is called from a different thread. For example, suppose you have a login dialog with OK button that sets the hourglass cursor, spawns a worker thread and returns; when the worker thread has received a response from the server, it removes the hour glass cursor and hides the dialog. In this case your event handling code would look as follows

import com.creamtec.ajaxswing.AjaxSwingManager;

void btnOK_actionPerformed(ActionEvent e) {
    // tell AjaxSwing that the logic operation will span multiple threads
    AjaxSwingManager.beginOperation();

    setHourglassCursor();
    // spawn worker thread
    new LoginWorkerThread(this).start();
}

... and the worker thread's run method will look like this

public void run() {
    // do business logic to login into the server
    doLogin();

    // Tell AjaxSwing that the logic operation that was started in the event handler is finished
    AjaxSwingManager.endOperation();
}

When run in AjaxSwing virtual runtime environment, the above code will cause AjaxSwing to wait even after btnOK_ActionPerformed method has returned until endOperation is called in run method of the worker thread. When run as a stand alone application without AjaxSwing, beginOperation and endOperation methods will do nothing. Notice that AjaxSwingManager class is distributed separately so that you can package it with your application when you distribute it without AjaxSwing. See JavaDoc on AjaxSwingManager for programming details.

Asynchronous Updates thru Server-side push

Instead of blocking the response with begin/end operation calls, another alternative is to use server-side push to send updates to the browser asynchronously. This can be useful to display something like auto-updating clock or implement a web chat. AjaxSwing supports server push thru router.updateInterval property which can be programmatically controlled using ClientAgent.setUpdateBrowserInterval method. When set to a non-0 value, it instructs the browser to check for updates on the server at the given interval. If the server has updates, they are sent to the browser as part of the response, without the user having to click anything in the user interface. The developer can request full page refresh by calling
ClientAgent.getCurrentInstance().setUpdateBrowser(true);
or for better performance explicitly tell AjaxSwing which components should be updated in the browser using AbstractDocument's setComponentDirty() method as shown below:
ClientAgent.getCurrentInstance().getHTMLPage().setComponentDirty(myComponent, true);
See TabbedDialog.java in AjaxSwing/src folder for a complete code example, or open WebMailDemo in the browser, open Preferences dialog, go to Update Interval tab, enter the number of seconds for update interval and click apply to see it in action.

Associating threads to client agents

Every browser session is represented with an instance of ClientAgent. AjaxSwing automatically associates threads launched from the main() method with the instance of the client agent, and it provides Swing with agent-specific EventQueue and EventDispatchQueue to make sure that virtual web users running in the same JVM do not conflict with each other. However, sometimes threads are started outside of AjaxSwing managed context and AjaxSwing is unable to determine which client agent is associated with a thread. If a thread is reused or shared in the application code, this can lead to events delivered to the wrong client agents and consequently web users.

If no or wrong client agent is found for a given thread, consider using ClientAgentManager.setMyAgent() to associate a particular thread with the instance of the client agent when the thread is created or used from another thread that has a valid association to the client agent. You can always look up the client agent using ClientAgent.getCurrentInstance().

Summary

Differences between GUI and HTML models may prevent certain Java applications using multithreading from being rendered correctly. AjaxSwing exposes a simple API that can be used to control when the rendering occurs. Integration with this API is easy and does not prevent the applications and applets from being run in a stand- alone mode as before.