|
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 CreamTec 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.
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.
|