Sprinkle Some AJAX Magic in Your Struts Web ApplicationAJAX is the latest revolution in web development circles, allowing rich dynamic interfaces deployed within a normal web browser. Struts has been one of the de facto standards for Java-Web development for a number of years, with a large number of applications already deployed. This article will show you how to combine the richness of an AJAX user interface with your existing Struts applications.
This article shows a simple and elegant way to do this by including a couple of lines of JavaScript on your JavaServer Pages (JSPs). While we show how to reuse existing Struts actions, the techniques are equally applicable to the Java-Web framework of your choice. The method proposed will also allow a move to the next version of Struts (Shale) or JavaServer Faces (JSF) in the future.
AJAX stands for "Asynchronous JavaScript and XML." It is a technique, rather than a framework (such as Struts). The reason for the buzz around it is that it allows web pages to behave less like flat documents and more like dynamic GUI apps that users might expect from their desktop environments. AJAX techniques can be used for all recent browsers (including Internet Explorer and Netscape/Mozilla). It is already used by (among others) Microsoft (for its Outlook web client) and Google (for Google Maps and Gmail).
Most current Struts applications follow the standard "web page as a flat document" structure. If you wanted to mimic the behavior of GUI desktop apps (such as those built using Java Swing, Visual Basic, or Delphi) you had two choices: you could either send all of the information that might possibly required as part the web page with (a lot of) JavaScript to handle the dynamic behavior (a slow and not very enterprise-Java way to do things), or you could do constant form submits back to the server (an effective, if somewhat clunky, method). AJAX gives you the best of both worlds: dynamic web pages, but with most of the application running in Java on your web server.
AJAX is similar to existing Dynamic HTML techniques, with the addition of a "background" call to the server to get new/updated information as required. The mechanics of AJAX have already been covered in detail elsewhere--take a look at the Resources section at the end of this article for some good examples. The minimum you need to know is:
XMLHttpRequest (or
Microsoft.XMLHTTP ActiveX object if you are using
Internet Explorer). These objects can be called from the JavaScript
on your web page. They allow you to request content from your web
server as a background call (i.e., the screen does not "go blank" as
usually happens during a form submit).XMLHttpRequest and
Microsoft.XMLHTTP objects return can be treated as
either XML or plain text. JavaScript (on your web page) can then
update the page with this new content as required.onclick, onchange,
onblur, etc.The chances are that if you are reading this article, then you are interested in AJAX's ability to create dynamic web interfaces and would like to know how to add it to a Struts application. What are your options if you want to do this?
Some other advantages of our preferred option are:
How do we actually implement our chosen solution? We start by reminding ourselves how a "standard" (non-AJAX) Struts application works. In this application, the normal flow of events is as follows:
Action,
generating the web page.ActionForm class containing the posted data.Action that then processes the request (e.g., saves
the data to a database).A simple Struts application demonstrating this flow of events can be downloaded here: struts-non-ajax.zip. This application, based on the sample applications provided with Struts, either hides or displays blue and green tables depending on the values entered by the user. Figure 1 shows the screen on initial page load. Figure 2 shows the screen after the user has entered values and pressed Submit. Although simple, it is enough to demonstrate a Struts application at work.

Figure 1. Non-AJAX sample: Initial screen

Figure 2. Non-AJAX sample: Values entered, Submit
pressed
The server-side code is as you would expect: a Struts
Action that forwards to the (same) JSP using the
values defined in struts-config.xml. Some other points to
note in this code sample are:
http://localhost:8080/struts-non-ajax/ (or
the equivalent in your web server) to index.jsp.showBlue and showGreen). The page also contains
<logic:equal> tags, but as the values for these
text boxes are initially blank, the content within them is not
displayed.SampleAction
class.SampleAction logs the values, and then forwards back
to index.jsp. A more sophisticated Struts application would
do more, such as saving to or retrieving from a database.showBlue or
showGreen are true, the tables will be displayed.There is nothing "wrong" with this application. After all, similar Struts projects have been deployed for years. But how do we to add dynamic behavior to this application, without adding complex JavaScript or continual form submits?
|
Take a look at the Figures 3 and 4 below. At first glance, they seem similar to our previous ones. The difference is that after then screen loads (Figure 3) and the values in the textboxes are changed, the screen automatically updates without the screen going blank, giving the result as per Figure 4. The normal Submit button is also still there, should you choose to use it.

Figure 3. AJAX sample after page load

Figure 4. AJAX sample after AJAX call
Adding this AJAX behavior is surprisingly easy. The server-side
code is the same as usual: a Struts ActionForm to hold
the data, and a Struts Action that performs the tasks
required (e.g., database access) and then forwards to the
appropriate JSP to display the result.
<span> tags, giving
each an id.onchange() method of a textbox), call the
retrieveURL() function, passing in the URL to the
Struts Action that will do the necessary server-side
processing.Action to forward back to the same JSP. In our
sample, we trigger the AJAX call in the onchange()
method of the showGreen/showBlue textboxes.The JavaScript function retrieveURL() calls Struts
on the server (via the URL provided), takes the JSP response, and
updates the web page being displayed, where the
<span> tags on the existing web page match those
on the newly returned JSP. Simple!
<span> tags to the JSP page, which mark
sections that will be updated during AJAX calls.We will look at each of these in more detail.
retrieveURL() function takes a parameter of
the URL of the server and the name of the Struts form. The URL will
be called using AJAX and the values of the form passed to the
server.getFormAsString() is a function that converts the
values on the form named in retrieveURL() into a
query string suitable for posting to Struts on the server.To use, simply add the retrieveURL() function to
the onclick()/onChange() method of the
event you wish to trigger the screen update.
There are some interesting items going on in both methods.
Within the retrieveURL() method, the line
req.onreadystatechange = processStateChange (note: no
brackets) tells the browser to call the
processStateChange() method (which we talk through
later in this article) once the server sends back its response.
This method (now standard in AJAX) also determines whether it
should use the Internet Explorer (ActiveX) or Netscape/Mozilla
(XmlHttpRequest) object to ensure cross-browser
compatibility.
The getFormAsString() method converts the HTML form
into a string to be appended to the URL (which allows us to do a
HTTP GET request). This string is escaped (spaces are converted to
%20, etc.) and is in a format that Struts can use to
populate an ActionForm (without Struts being aware of
the special AJAX nature of the request). Note that while we use a
HTTP GET in this sample, it would be equally possible to use a HTTP
POST by looping in a similar manner and adding the form fields to
the request.
function retrieveURL(url,nameOfFormToPost) {
//convert the url to a string
url=url+getFormAsString(nameOfFormToPost);
//Do the AJAX call
if (window.XMLHttpRequest) {
// Non-IE browsers
req = new XMLHttpRequest();
req.onreadystatechange = processStateChange;
try {
req.open("GET", url, true);
} catch (e) {
alert("Server Communication Problem\n"+e);
}
req.send(null);
} else if (window.ActiveXObject) {
// IE
req = new ActiveXObject("Microsoft.XMLHTTP");
if (req) {
req.onreadystatechange=processStateChange;
req.open("GET", url, true);
req.send();
}
}
}
getFormAsString() is a "private" method used by the
retrieveURL() method.
function getFormAsString(formName){
//Setup the return String
returnString ="";
//Get the form values
formElements=document.forms[formName].elements;
//loop through the array, building up the url
//in the format '/strutsaction.do&name=value'
for(var i=formElements.length-1;i>=0; --i ){
//we escape (encode) each value
returnString+="&"
+escape(formElements[i].name)+"="
+escape(formElements[i].value);
}
//return the values
return returnString;
}
|
So far, we have looked at the JavaScript to do the AJAX call
(listed above) and the Struts Action,
ActionForm, and JSP (mostly the same, with
the addition of <span> tags). To complete our
understanding of the Struts-AJAX project, we need to look at the
three JavaScript functions that are responsible for updating the
current web page when the results from the server are received.
processStateChange(): The method name that we set
before making the AJAX call. This method is called by the
XMLHttpRequest/Microsoft.XMLHTTP object
once the server has completed sending back its response.splitTextIntoSpan(): Loops through the response,
picking out an array of <span
id="someName">NewContent</span> elements.replaceExistingWithNewHtml(): Loops through this
array of span elements, searching for
<span> elements in the existing
page with 'someName' and replacing them with the new
content from the server. Note that we get the returned content via
req.responseText for robustness (since it allows us to
manipulate any text response), rather than
req.responseXml (which is more powerful, but requires
that you return valid XHTML or XML).
function processStateChange() {
if (req.readyState == 4) { // Complete
if (req.status == 200) { // OK response
//Split the text response into Span elements
spanElements =
splitTextIntoSpan(req.responseText);
//Use these span elements to update the page
replaceExistingWithNewHtml(spanElements);
} else {
alert("Problem with server response:\n "
+ req.statusText);
}
}
}
replaceExistingWithNewHtml() is a "private" method
used by the processStateChange() method.
function replaceExistingWithNewHtml
(newTextElements){
//loop through newTextElements
for(var i=newTextElements.length-1;i>=0;--i){
//check that this begins with <span
if(newTextElements[i].
indexOf("<span")>-1){
//get the span name - sits
// between the 1st and 2nd quote mark
//Make sure your spans are in the format
//<span id="someName">NewContent</span>
startNamePos=newTextElements[i].
indexOf('"')+1;
endNamePos=newTextElements[i].
indexOf('"',startNamePos);
name=newTextElements[i].
substring(startNamePos,endNamePos);
//get the content - everything
// after the first > mark
startContentPos=newTextElements[i].
indexOf('>')+1;
content=newTextElements[i].
substring(startContentPos);
//Now update the existing Document
// with this element, checking that
// this element exists in the document
if(document.getElementById(name)){
document.getElementById(name).
innerHTML = content;
}
}
}
splitTextIntoSpan() is a "private" method used by
the processStateChange() method.
function splitTextIntoSpan(textToSplit){
//Split the document
returnElements=textToSplit.
split("</span>")
//Process each of the elements
for(var i=returnElements.length-1;i>=0;--i){
//Remove everything before the 1st span
spanPos = returnElements[i].
indexOf("<span");
//if we find a match, take out
//everything before the span
if(spanPos>0){
subString=returnElements[i].
substring(spanPos);
returnElements[i]=subString;
}
}
return returnElements;
}
By adding the above JavaScript code to our application, the following steps now happen on the server and on the browser.
onChange() event, which calls the
retrieveURL() JavaScript function.Action on the server, passing in all of the form variables in a
way that Struts will understand.processStateChange() method.processStateChange() method is called.<span>elements in the (new) server response.
Where it finds a <span> in the existing page
with the same name, it updates it with the new content.The JavaScript outlined above can cope with the way Struts is used in most applications, including those that are much more complex than our simple example. However, you may find that following the points below makes it easier to write and use your code:
Action and JSP for the initial request
(i.e., show full page) and the AJAX (update part of page)
requests.Action (controller) class, decide which
sections of the JSP page (all of the JSP or only part of it) need to be sent to the
browser. By setting flags in either the web server
session or ActionForm, the JSP page knows
which sections need to be rendered.<logic:equal> or
JSTL tags to decide if we need to render a section of HTML or
not.An updated version of this project, with AJAX enabled, can be downloaded here: struts-Ajax.zip
AJAX techniques promise to completely revolutionize how we build and use web applications. This article showed a simple technique to add AJAX behavior to existing Struts applications. It allows us to reuse our existing investment, not only in code but also in developer skills. As a nice by-product, it also allows us to write cleaner, more reusable, Java Struts applications.
Paul Browne has been consulting in enterprise Java with FirstPartners.net for almost seven years.
|
|