|
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
routine, 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 three alternatives that
can provide a solution to multithreading issues.
Manipulating AjaxSwing application properties to accommodate multithreading
The easiest way to solve asynchronous response related problems is achieved
by adding properties to AjaxSwing application properties file.
The application properties file is created either by AjaxSwing Console or manually and by default
is located in the tomcat/webapps/ajaxswing/conf directory. AjaxSwing allows
you to refer to windows and components by their "logical" names.
For details of naming read Referring to windows and components in AjaxSwing.
A logical name of a window/button is comprised by taking the window title/button
text, trimming any leading and trailing spaces, and then replacing any spaces
with '_' (underscore). To refer to a window in the application properties file
one should use window.<window-title>, and to refer to a component
one should use window.<window-title>.<component-type>.<component-title>. For
example, if you have a login dialog with the title "Login to server"
with a button "OK" then the references in the properties file would be
window.Login_to_server
window.Login_to_server.button.OK
Now that we know how to refer to components, let's see how we can put that to
use. Suppose that your window gets updated dynamically after it has been displayed.
Most common examples include a progress bar or some sort of a status window that
is updated by a background running thread. AjaxSwing will render an HTML page that
will capture the current state of the window, but the subsequent updates will not be
reflected. The easiest way to solve this problem is to add either autoRefresh
or autoSubmit properties to your configuration file. You can specify the number
of seconds after which the page should be automatically refreshed or updated so the
user will be informed of the progress. The difference between the two is that autoRefresh
ignores any text entered by the user while autoSubmit sends the currently entered data
to the server before updating the page.
In another scenario, suppose that when OK button is pressed the actionPerformed listener spawns a new
thread and returns immediately. When the new thread finishes the login
operation, it hides the Login dialog and displays the main frame titled
"Main Frame". What we will want to do is tell AjaxSwing that even
though the listener returns, the operation is still active and we only want it
to finish when a window with title "Main Frame" became visible. Note
that the title matching is done with Java startsWith function, which
means that even if the title is "Main Frame of my application" it will
still match. This can be accomplished by adding the following line to the
property file
window.Login_to_server.button.OK.action=Main_Frame
Other possible values to action key are wc_next_window and wc_new_window.
wc_next_window means that the operation ends when any other window, newly
created or previously shown, becomes visible on top of the currently focused
window. wc_new_window means that the operation ends when a new window is
created. Examples of these predefined values are shown below
window.Login_to_server.button.OK.action=wc_next_window
window.Login_to_server.button.OK.action=wc_new_window
So far action key for buttons is the only supported configurable
operation. For more flexibility see the next solution.
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.
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.
|