ou’d have to be developing web pages on another planet not to have heard the buzz about AJAX (asynchronous Java and XML). Judiciously employed, AJAX calls can enhance the usability of a site and add functionality to create eye-catching browser client applications. This article will show you how to use AJAX to invoke Struts actions and use Tiles to facilitate the creation of the response that updates the calling page. If you’re new to Struts, Tiles, and Javascript you’ll probably want to get a little background by checking out some of the tutorials or publications noted in the resources section. However, if you’re up to speed on how to write Javascript and you’ve got some Struts and Tiles experience under your belt then you’re ready to read on.
AJAX is actually not a new technology. XML-RPC calls have been around for years. Moreover, web services using SOAP received a lot of attention for a while but never reached the level of hype and intensity that AJAX has for a variety of reasons. It’s hard to pinpoint any single benefit of AJAX that is the cause for such energy in the development community; however, the growing awareness of the benefits of service-oriented architectures and the powerful user interface improvements that AJAX facilitates certainly has something to do with it.
The traditional web request-response model forces a web form submission and a reload (refresh) of the entire page in order to change the data a user is viewing. In contrast, AJAX provides a mechanism for in-place modification of a subsection of the page that often is faster and less of a context shift for the user. Modern browsers create a parse-tree of all the elements on a page (this is the Document Object Model or DOM) and AJAX response processing can selectively update a subsection of the DOM, avoiding an expensive, full reload of the page from the server. The Dojo toolkit (see resources section, in the left column, for download link) is a powerful Javascript API for invoking remote resources (URLs for our purposes) and also provides a robust set of ancillary Javascript utility classes.
What You Need |
–Dojo, which may be freely downloaded from http://sourceforge.net/project/showfiles.php?group_id=12867&release_id=67726. –Basic understanding of AJAX including knowledge of Javascript and XML parsing –Any Java Web container such as Tomcat or JBoss (Servlet spec 2.4+). –Some knowledge of the Jakarta Struts Web framework, the Tiles Web framework, and their associated taglibs. |
The ability to invoke a remote resource without a full page reload means that user interactions with a page’s data can be incremental. A Web page typically uses the form tag to mark off a portion of the page to submit back to the server. The fields of the form are posted to the resource specified in the action attribute of the form tag. While Dojo does support sending or submitting a ‘form node’ to refresh/replace data on a page, this isn’t required. (The code samples I’ll be discussing shortly will demonstrate this.) The HTML spec also provides a div tag that allows for a non-form tag delimited node to be demarcated on a page. The data in the div is what is typically updated or replaced when the AJAX response is received from the server.
Dojo configuration is fairly simple. The main dojo.js Javascript resource file sits on your web server and you reference it in your pages. Including a Javascript block like the one below tells Dojo where to “find itself.”
var djConfig = { baseRelativePath: "js/dojo", isDebug: true, preventBackButtonFix: false };
The isDebug flag, when set to ‘true,’ forces useful debug information to print to the page being loaded. The /src directory resides where this base path points ensuring that Dojo can access the libraries you reference in your scripts.
A base template can be defined in the tiles-def.xml to specify Dojo and other Javascript resource files in a reusable way. Other tiles can reuse this ‘root’ tile by extending. Below is the ‘root’ tile’s definition.
The basic.jsp tile (see the sample code [[will add link]] accompanying this article) includes a Struts logic tag to read the argument that the tiles-def.xml ‘putList’ element passes to the tile.
" type="text/javascript">
Other tiles extending this base tile automatically include the appropriate Dojo Javascript resource references by passing the desired script names as additions to the reusableJavascripts list in their own tile definition in the tiles-def.xml.
Using Tiles to add the Javascript resource file references reduces the likelihood of typos or incorrect locations being specified inadvertently. It also makes your application more maintainable because it centralizes these definitions so developers don’t have to look at individual Javascript pages to determine the .js resources being utilized.
As an aside, Dojo configuration for local development (without a web server) requires a slightly different approach. Be advised that the notation for the URI to the local Dojo resources (dojo.js) is browser sensitive. (I found this out the hard way.) The markup below (with comments) sums it up.
A Sorted Example
In order to understand how Dojo works with Struts and Tiles it will help to have a concrete problem to solve. To this end, the accompanying sample code introduces a list of famous people and displays it to the user as shown in Figure 1.
|
![]() Figure 1. This is what you should see after the Dojo call is invoked to sort the list. |
![]() Figure 2. The list has been quickly reordered based on the “Ascending” selection in the drop-down, which invokes the AJAX call to re-sort the data without a server trip. |
- Dave Thomas
- Ronald McDonald
- George Washington
This div markup, however, is the generated HTML?to avoid having to repeatedly create this snippet I’d much prefer to use a Tile to make the whole block easy to reuse and less prone to error. Something like this would be preferable:
In the sample code there is a cached list of these users in a session attribute called “famousFolks.” This list will be resorted by the servlet that Dojo invokes using a standard Struts mapping to the URL (shown below).
The newly sorted list is passed to sortUpdateXml.jsp. This JSP contains a
Let’s fill in a little more functionality. The page that holds the AJAX call also includes a drop-down list field called sortBy that specifies an onchange event Javascript method call. The drop-down markup looks like the snippet below, but please check out the sample code for the full version.
When a new sort parameter is selected from the drop-down list the doSort( ) Javascript method defined in the
or included from a Javascript (.js) resource file is invoked. With this bit of context set, let’s now take a look at the code for the Dojo call.Dojo provides a varied and robust API that is generally speaking, intuitively packaged. With that said, it wasn’t immediately apparent to me that an AJAX call is set up using a method from the dojo.io package ?dojo.io.bind( ). (The ‘io’ must mean to and from the form. I would have preferred a .net package similar to Java’s.)
function doSort(sortTerm) { dojo.io.bind( { url: "famousPeopleSort.shtml", content: {sortedBy: sortTerm}, method: "POST", mimetype: "text/html", load: function(type, value, evt) { processReturnValue(value); }, backButton: function(){ //for maintaining Dojo back button stack dojo.io.bind({ url: "famousPeopleSort.shtml", content: {sortedBy: previousSortTerm}, method: "POST", mimetype: "text/html", load: function(type, value, evt) { resetDropDown(previousSortTerm); processAjaxResponse(value); }, error: function(type, error) { alert("Error: " + error); } }); }, error: function(type, error) { alert("Error: " + error.reason); } });
This method call sets the URL argument for an HTTP Post to the servlet mapped to famousPeopleSort.shtml in the struts-config.xml (shown earlier or see the sample code). The content term holds the sortTerm argument, passed by the drop-down list onchange event call to the function, and sets it as a request parameter of the Post. The method argument mimics the method attribute of the HTML form tag and the mimetype specifies the type of data expected in the AJAX response. The load argument defines a function to be invoked when the AJAX response is returned.
You also may have guessed from the above Javascript method that Dojo can maintain expected browser ‘back button’ behavior; it does so by creating its own call stack. This is useful because, of course, many users navigate backward using the back button. In such circumstances, Dojo-invoked actions would be lost without a full page reload and the missing behavior might be confusing. Instead, each Dojo call is stacked on top of the previous one. As the user clicks the ‘Back’ button, the topmost, bound (meaning it was invoked by the io.bind method) call is popped off the stack and consumed.
The stack is managed with the help of an HTML file (iframe_history.html) that resides in the same directory as dojo.js. It is worth mentioning that while I’ve had success implementing Dojo’s back button management features, other developers have reported some less pleasant experiences; if you encounter issues hopefully this anecdotal note will offer some comfort. 😉
While it is great to have some programmatic control of the back button, if your business stakeholders aren’t familiar with Dojo’s ‘back button’ behavior you ought to prototype/demo it for them before you get too far into development. AJAX calls create a very different navigation and page flow paradigm from what might be “expected.” Moreover, if something unknown (or that can’t be seen) is happening that makes your site appear unresponsive, then users are usually unhappy and your application isn’t likely to be successful. Therefore, you might want to consider adding a progress indicator or similar visual cue that starts up when the Dojo call is invoked and goes away when it is completed.
Before discussing how the returned data is processed I want to look at the response document that holds it. The response document for my famous personages sort is in XML format and acts as a container for the JSP tile referenced by the tile insert statement in the div described earlier.
<%@page contentType="text/xml" %><%@taglib prefix="tiles" uri="/WEB-INF/tiles.tld" %><%@taglib prefix="html" uri="/WEB-INF/struts-html.tld" %><%@taglib prefix="bean" uri="/WEB-INF/struts-bean.tld" %> ]]>
Because the tiles:insert is processed on the server, the Dojo call to the sorting action will rework the list and integrate it into the HTML created by the tile. When the AJAX call is made, this dynamically-generated HTML will be nested in the ajax-response XML element for processing by the Javascript function specified in the bind( ) method.
function processResponse(response) { if (djConfig["isDebug"]) { dojo.debug("ajax response: " + response); } //if the XML element ?ajax-response? isn?t found then write out what was returned if (response.toLowerCase().indexOf("") < 0) { document.write(response); document.close(); return; } //parse the AJAX XML response into a document. var xmlDoc = dojo.dom.createDocumentFromText(response); //refer to the XML response above ? the ?field? element is inside //the response document. var fields = xmlDoc.getElementsByTagName("field"); for (var i = 0; i < fields.length; i++) { var id = fields[i].getAttribute("id"); var attribute = fields[i].getAttribute("attribute"); var value = null; if (fields[i].hasChildNodes()) { for (var j=0; j
The processing of the response occurs during the parsing of the returned XML document. The code above finds the field element in the response and extracts its contents. It then replaces the div on the page with the specified #cdata-section value of the response. The replacement is an update to the DOM, of course, and the browser updates the display to reflect the change of content. This was the only cross browser parsing approach I could get working though I'm sure there are others out there.
Struts and Tiles have been key frameworks in enterprise Java development for years. AJAX and the Dojo toolkit are an excellent combination for using Struts and Tiles in a granular, service oriented manner that separates concerns and promotes the reuse of both code and display elements. While there is a learning curve to integrating these powerful frameworks, the payoff is worth the effort.