Implementing the asynchronous data loading mechanism in rich client applications
Wei Wang
IBM Software Group
Software Engineer
Beijing, China
April 2009
Abstract: This article introduces the concept of asynchronous data loading and how to apply this mechanism in both Web applications and rich client applications. A concrete design is provided to illustrate how to implement asynchronous data loading in an Eclipse rich client application.
Introduction
A Web application or a client/server application must be able to present data from a remote data repository such as a relational database or an IBM® Lotus® Domino® server. While the application is fetching data, however, the user interface (UI) usually hangs, refusing to respond to any user operations.
Fortunately, now Web applications can use Asynchronous Java™Script and XML (AJAX) to send asynchronous XML HTTP requests to the server to fetch data, execute business logic, and partially refresh the Web page. We can also implement this mechanism for client/server applications.
When the application needs to fetch data, it initializes and schedules a non-UI thread (or Job, within Eclipse framework) to fetch data, allowing the user to continue using the application without being interrupted by data-fetching operations. When data fetching finishes, the UI thread will be blocked, and the UI will get updated to display the newly loaded data.
The concept of asynchronous data loading
When we use the term “asynchronous”, we mean that two different things happen without interrupting each other. In the context of this article, it means that UI operations run without being interrupted by data-fetching operations.
When a UI application is running, it presents data to end users, often in the form of a data list/tree. If there is a large quantity of data, it may require a lot of memory to hold the entire amount of data ready in the application. Thus the prevalent solution is to only partially load the data.
When the user wants to see more data, the application will launch the course of data fetching and present the rest of the data. There are two ways of doing this:
- In the first, UI waits for the data and will not respond until data is ready.
- In the second, the UI continues to respond to user operations and presents the requested data when the required data is ready.
Obviously, the second solution is considered better because it’s more user friendly.
Web application development vs. rich-client application development
Applications can be roughly divided into two categories: Web applications with browser/server architecture, and rich client applications with client/server architecture.
Web application development
As for Web applications, most of us have experienced the transition between the classic application model and AJAX-based model, as shown in figure 1.
Figure 1. User interaction handling between the classic and AJAX-based models
In the classic model, when a user submits a form or clicks a link, an HTTP request is sent to the Web server, which generates the target document according to the request parameters and returns the whole page to the browser. In this case, the browser renders the new page.
In the AJAX model, an HTTP request is sent by the AJAX engine with the intent of fetching data in XML format. The AJAX engine obtains the data and invokes the JavaScript callback function to partially update the page.
So, we can see that the core differences between these two models are twofold; the first is page versus data, and the second is full-page refresh versus partial update.
In the first, an AJAX-based application has better performance compared with the classical application, because it only needs to fetch data from the remote server and interweave the data with display elements, for example, a cascaded style sheet (CSS) configuration, which is stored on the browser side. Thus a lot of resources are stored on the browser side, without the need to fetch them each time a request is handled, so network bandwidth is used more efficiently.
In the second, AJAX -based applications are more interactive; since only part of the page is refreshed, the other parts will still be available to the user. Thus the user is able to trigger several requests in very short time. There’s no possibility of achieving this in the classic model.
The asynchronous data loading mechanism is realized via the AJAX model, and we have seen this model bring great value to current Web 2.0 applications.
Rich client application development
The typical rich client application is a stand-alone executable with a graphical interface composed of several controls for the user. We could regard our Lotus Notes® client as a combination of many complex rich client applications; for example, let’s take a look at the Inbox view of Notes 8.5 (see figure 2).
Figure 2. Typical rich client application
Here we see three views: The Mail Navigator view and Follow Up view located on the left-hand side, and the Mail List view in the center.
Typically the Mail List view must present thousands of messages, so it’s not a good idea to load all the messages into memory when the application is launched. If the data to be rendered isn’t in the memory, the application needs to fetch the data and
then display the newly fetched data.
If the work of data displaying and fetching occurs sequentially and the data fetching takes a relatively long time, the user must wait for the data and can’t do anything else during this process.
So, we need to implement a mechanism, similar to that in the AJAX model, whereby a backend thread is launched to fetch the data and, once the data is ready, a callback is invoked to update the UI (see figure 3).
Figure 3. Backend thread for data fetching
This model could be applied in almost all client UI applications; however, in this article we present one concrete design within an Eclipse platform as an example.
Eclipse provides its own multi-thread framework, in which we usually create a Job to represent a thread. A normal Job could be regarded as a non-UI thread, while a UIJob could be considered as a UI thread.
In the application’s main thread, which is also the UI thread, a non-UI job can be created and scheduled to fetch the data. When the data is ready, a UI job can be scheduled in the non-UI job to execute the UI update operation.
A class design skeleton is provided below as a typical implementation, but first we need to define a couple of classes and interfaces.
First, the context information and other parameters should be organized into a class, such as FetchEvent (see listing 1).
Listing 1. FetchEvent class
Public class FetchEvent {
private int fetchCount;
private FetchFinishListener listener;
private int direction;
private int startOffset;
……
}
where the class member fetchCount indicates how many records are to be fetched, and the member direction indicates how to fetch the data according to the start offset.
FetchFinishListener is an interface that declares the callback functions to be implemented by concrete data fetching logic:
public interface FetchFinishListener {
void fetchPaused();
void fetchCompleted();
}
The method fetchPaused() above is invoked when the data fetching process terminates abnormally, for instance, when a network problem prevents the data from being fetched successfully.
The method fetchCompleted() is invoked when the data is successfully fetched from the remote repository.
In the snippet below, the method postCollectedOperation() is invoked when data is already fetched:
interface IPostOperation() {
IStatus postCollectedOperation(Object data);
FetchEvent getFetchEvent();
}
Now let’s discuss the Job hierarchies. A UI job needs to be defined, to execute the UI update work (see listing 2).
Listing 2. Job classes defined
class PostponedUIJob extends UIJob{
private Object data;
protected IPostOperation postOperation;
PostponedUIJob(IPostOperation p) {
postOperation = p;
}
void setData(Object data) {
this.data = data;
}
public IStatus runInUIThread() {
postOperation. postCollectedOperation(data);
}
}
PostponedUIJob is defined as the UI Job that will be scheduled after the data fetching finishes.
The abstract class shown in listing 3 is used to create a non-UI Job to fetch data and then schedule the UI Job to update the UI. Note that, after the UI Job is scheduled, the method join() is invoked to wait until the UI is updated.
Listing 3. Abstract class for non-UI Job
abstract class FetchJobAndPostUI extends Job{
protected DeferableUIJob deferableUIJob;
protected IPostOperation _postOperation;
private Object data;
public FetchJobAndPostUI (IPostOperation p){
super(
“Fetch Job and Post UI Job”);
_postOperation = p;
postponedUIJob = new PostponedUIJob (a,p);
final IPostCollection p1 = p;
addJobChangeListener(new JobChangeAdapter(){
public void done(IJobChangeEvent event) {
if(event.getResult().getSeverity() == IStatus.
OK) {return;}
if(p1 != null){
if(p1.getFetchEvent() != null){ p1.getFetchEvent().getFetchFinishListener().fetchPaused();
}
}
}
});
}
protected abstract IStatus fetch();
public IStatus run(){
IStatus status = fetch();
if(status != Status.
OK_STATUS){
return status;
}
postponedUIJob.schedule();
postponedUIJob.join();
return Status.OK_STATUS;
}
}
In a concrete implementation for the abstract class FetchJobAndPostUI, the abstract method fetch() is implemented. Usually, the application will create different fetch events for different fetch operations, and the parameters in the fetch events will determine which data will be fetched.
Meanwhile, the method postCollectedOperation() in the interface IPostOperation should be implemented. Usually, the implementation invokes FetchEvent.fetchCompleted() and also allows the application to customize in other ways, such as setting selections according to the status of the data on the server side.
Conclusion
This article provides an analysis for the implementation of asynchronous data loading in both Web and rich client applications. A concrete class design is used to illustrate how to implement asynchronous data loading in an Eclipse Rich Client Platform (RCP) application.
We compared the similarities and differences between the implementation of asynchronous data loading in browser/server applications and client/server applications.
The two are similar in that a non-UI thread is launched to fetch pure data from a remote server, and a client-side callback function is executed to update the UI after the data is returned. Also, in the design for the rich client application, the method “fetchCompleted()” defined in FetchFinishListener is a callback function.
Another similarity is in the support for partial UI refresh; in AJAX-based applications, the callback function can update specific sections in a Web page. For a rich client application, the render engine can also redraw only part of the display area of its UI.
As for the differences, in browser/server applications, the response from the server is always in XML data format, unlike in the case of client/server applications. Secondly, a mutual exclusion (mutex) management mechanism is embedded in the client/server application development model, whereas there is no such mechanism in the browser/server application development model.
In the application design introduced above, we could configure a mutex rule for each data fetching job, to prevent two data fetching jobs from occurring simultaneously. This is an embedded mechanism within an Eclipse framework.
Resources
About the author
Wei Wang is a developer on the IBM Lotus Expeditor team in Beijing, China. Most recently he has been developing new features as well as fixing bugs for Expeditor table control. His background also includes extensive experience as a Java server-side developer.