Eric is a consultant in New York, and has worked extensively in Java and C++ developing real-time trading and financial applications. He can be contacted at [email protected].
HTML was created to enable the publication and display of documents within a specialized browser application. The real power of HTML is its ability to link objects (text and images) on one document to otherentirely separatedocuments. The end result is a global set of document pages that connect to one another much like a web; hence the name, "World Wide Web." More important, HTML describes its content and its visual formatting in a manner independent from the actual viewer application.
This abstraction led to HTML's immediate popularity, as people were able to create content to be displayed equally on any computer, with any operating system, anywhere on the globe. Despite its popularity, HTML suffers from one drawbackit's static by nature. In a world full of cheap computing power and otherwise rich desktop applications, static web-based applications seem primitive. It wasn't long before the two most popular web browsers of the time, Netscape Navigator and Internet Explorer, added support for scripting languages such as JavaScript. With the addition of script, a web page could be updated in the browser without the need to request a new page from the server. This began an age of dynamic web pages, much like those we see today that contain popup menus, tool-tips, and the like.
Modern browsers make their HTML content available to embedded script code through an object hierarchy called the Document Object Model (DOM). A page's script can modify its HTML by manipulating parts of the DOM. By including islands of XML data within the HTML, a page's script can show or hide portions of the page and its data based on user actions. This technology is known as Dynamic HTML (DHTML). To avoid embedding potentially large amounts of XML data within a single page, Microsoft added the XMLHttpRequest object to its Internet Explorer. This object can be used to dynamically make an HTTP request to the server, receive XML as the response, and use that XML to update portions of the currently displayed page.
Introducing Ajax
Although the individual techniques and capabilities have been around for some time, the use of HTML, XML, JavaScript, and the XMLHttpRequest object to form a dynamic web-based application has more recently become known as "Asynchronous JavaScript and XML" (Ajax). The name defines the design pattern commonly used to create dynamic web pages, and has helped to define a common model that many browsers now support, such as Mozilla Firefox, Microsoft Internet Explorer, Opera, Konqueror, and Apple Safari.
How much impact can Ajax really have on a web application? To answer this, you need to witness it for yourself. One of the strongest demonstrations of the usefulness of this technique in web design is the application, Google Suggest (notice I didn't call it a web site). Start your browser and go to the beta version of Google Suggest (http://www.google.com/webhp?complete= 1&hl=en). Think of an obscure word or phrase to search for, and type it into the edit box on the page. As you type each letter, a list appears below the edit box that contains the best 10 matches (words and/or phrases) for what you have typed; see Figure 1. As you type each letter, the suggestions are refined until, more than likely, the word or phrase you were going to type is right there in the list. Simply navigate to the entry in the list with the mouse or keyboard and save yourself some typing.
Obviously, Google does not deliver a dictionary of words with the initial Google Suggest page. So how does this work? Ajax is used to make asynchronous requests to the Google servers with the letters you've typed. As you continue to type, response data is received, and the list on the page is updated dynamically. This interaction does not impact you negatively in any way; you don't need to wait for the responses; you don't wait while the entire page is refreshed; and the results are useful. Compare this to the more simple interaction that takes place with a static web application.
Figure 2 shows the basic interaction between a web browser and a web server:
- The browser makes an HTTP request to the web server.
- The web server returns HTML to the browser over HTTP.
- The browser renders the HTML, and waits to repeat this cycle.
With Ajax, the pattern of communication between the browser and the web server is more involved than with a static web page. There is a paradigm shift in the notion that after the initial page is delivered, the HTML lives at the client, not the server. The server merely delivers raw data in the form of XML that the client uses to update portions of the HTML already on display. Figure 3 shows the more complex interaction that occurs between the Ajax client and server:
- In response to a user event, JavaScript on the page makes a request to the server, using XMLHttpRequest.
- The XMLHttpRequest object sends the special request over HTTP to the server.
- The server (a web or application server of some sort) receives the request, retrieves some data, and returns it to the client over HTTP, formatted as XML.
- The XMLHttpRequest object provides the data to a JavaScript function on the page.
- JavaScript on the page updates a portion of the HTML with the data retrieved. For example, a list box may be filled; text entries may be validated, and so on.
Google Suggest is not the only example of Ajax in action. Other Ajax applications include:
- Microsoft Outlook and its web interface; arguably one of the first Ajax applications.
- Google Maps (http://maps.google.com/).
- Google Groups (http://groups.google.com/).
- Google GMail (http://mail.google.com/).
- Amazon A9 (http://www.a9.com/).
- Writely, a free online word processor (http://www.writely.com/).
- Flickr, a photo-sharing site (http://www.flickr.com/).
As a matter of fact, I'm writing this article using the Writely word processor.
Using Ajax
You can develop an Ajax application with nothing more than a browser and a web server that provides some sort of CGI support (such as Apache Tomcat), where you can run server-side code, such as PHP or Java. However, there are toolkits that make it easier to develop Ajax applicationsDojo (http://dojotoolkit.org/), GLM from SourceForge (http://sourceforge.net/ projects/glm-ajax/), and DWR from Getahead (http://getahead.ltd.uk/dwr/). I'm using DWR here because it's an Apache-licensed, open-source, Java-based toolkit, and I like its development paradigm.
DWR's development paradigm is interesting because it lets you develop server code as plain old Java objects (POJOs), which you can access from JavaScript within the browser. The JavaScript uses the server-side objects as though they were local; DWR uses Ajax as a proxy between the browser and the server. Nothing gets downloaded to the browser besides the HTML page that has JavaScript embedded. The DWR Java Servlet running on the server transparently maps the Ajax requests and responses to and from the POJOs you supply (Figure 6). DWR also integrates well with frameworks such as Struts, Spring, and Hibernate.
Sample Magazine Archive Viewer
The application I present here is a magazine archive viewer, meant to display article content from back issues of your favorite magazines, such as DDJ (Figure 4). To run the sample Ajax application, you need to download the DWR toolkit (http://getahead.ltd.uk/dwr/download/). You can download the toolkit's JAR file to add to an existing Java-based web application, a WAR file to deploy as its own web application, as well as the complete source to both. You will also need a Java Servlet-enabled server, such as Apache Tomcat (http://jakarta.apache.org/tomcat/). Finally, you can download the sample magazine viewer application (available electronically; see "Resource Center," page 6).
To create the web application, create a subfolder named "DDJViewer" in the webapps folder where you have Tomcat (or another Servlet container) installed. Copy the file, main.html, to this folder. Next, create a subfolder named WEB-INF within the "DDJViewer" folder. Copy the files dwr.xml and web.xml into this folder. Next, within the WEB-INF folder, create two subfolders named "classes" and "lib." Copy the file MagViewerImpl.class into the classes folder. Finally, copy the files dwr.jar and xalan.jar into the lib folder. The resulting directory structure, with proper file placement, should look like Figure 5.
The MagViewerImpl.java class delivers all of the magazine content to the caller. The methods are:
- getMagazines returns a list of magazines whose articles are available. In this sample, the choices are "Dr. Dobbs Journal," "C/C++ Users Journal," and "Software Development Magazine."
- getPublicationYears returns a list of years for which articles are available.
- getYearTopics returns a list of the monthly topics for the specified magazine and year.
- getIssueDetails returns a list of article titles for the magazine issue specified.
- getArticle returns the content of an article for the magazine issue specified.
The file, dwr.jar, contains the Java Servlet and supporting Java code for the DWR toolkit. The file, MagViewerImpl.class, is the sample class that implements the aforementioned methods, which are referenced in the JavaScript within the web application's HTML page. To instruct DWR to expose the methods of any class to the client, you need to add entries into the dwr.xml file. The contents of this file for the sample application are:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ...
<dwr>
<allow>
<create creator="new" javascript= "MagViewer">
<param name="class" value=
"MagViewerImpl"/>
</create>
</allow>
</dwr>
This file tells DWR which classes to allow the client JavaScript to create and access. In this case, the <create> and <param> tags specify that there is a Java class MagViewerImpl that the JavaScript can access as MagViewer.
Finally, there is the HTML file itself, main.html. This web page contains four drop-down list boxes, and one text field. The list boxes allow you to drill down to a specific magazine article, and the text box displays the article contents. Within the HTML <head> section, DWR requires you to reference the JavaScript that your web page will access. In the sample application, I use the standard DWR engine and utility script, dwr/engine.js and dwr/util.js, respectively. The application also references the script generated for our application, dwr/interface/MagViewer.js. These references are:
<head>
<title>DDJ Ajax Demo Application</title>
<script
type='text/javascript' src=
'dwr/interface/MagViewer.js'></script>
<script type='text/javascript' src=
'dwr/engine.js'></script>
<script type='text/javascript' src=
'dwr/util.js'></script>
</head>
The remainder of the page contains the JavaScript that forms the dynamic nature of the web application. The function init (Listing One) is set up as the function to call when the page is first loaded. This function tells DWR to display errors and warnings as pop-up alerts. This is something you may want turned on for debugging, but turned off when deployed to production. Next, the call to DWR's useLoadingMessage function tells DWR to display a message whenever an Ajax request for data is made to the server. The text "Loading..." will be displayed in red in the upper right-hand corner of the page as it waits for the Ajax response. Once the response is received, the message goes away.
Finally, the page's update function is called. Here, a call is made to the MagViewerImpl class's getMagazines method. In the Java implementation for this method, there are no parameters. However, in the JavaScript, the parameter createMagList is supplied. This parameter is not passed to the server, but is instead the name of the JavaScript function that will be called to receive the response data. Because each Ajax call is asynchronous and we don't want to block the page while it waits for data from the server, DWR requires that the first parameter to any server call be a callback function to handle the response (Figure 6). The callback function is defined with the single parameter, data, because it contains the data returned from the server. This code is:
function update()
{
<!-- createMagList will be called
with the data -->
MagViewer.getMagazines(createMagList);
}
<!-- This is an Ajax callback method -->
function createMagList(data)
{
DWRUtil.removeAllOptions("maglist");
DWRUtil.addOptions("maglist", data);
}
The result is that the first drop-down list is populated with the magazine names returned from the server. You can write this DHTML code yourself, but the DWRUtil class referenced in the JavaScript makes it easier to perform this task. Each drop-down list on the page has defined, within the HTML, a function to be called when a selection is made:
<select id="maglist"
onclick="populateYearList();"
style="vertical-align:top;">
</select>
In this example, when a selection is made from this drop-down list (which contains magazine names), the JavaScript function populateYearList is called. This function, in turn, calls the getPublicationYears method on the MagViewerImpl class, which results in the population of the second drop-down list. This pattern is repeated with the remaining drop-down lists; as each selection is made, a request is made to the server, and the next drop-down list is populated. Finally, once a specific article is chosen in the last drop-down list, the article contents are requested and displayed within the text box at the bottom of the page. The HTML for the drop-down lists and the text box can be seen in Listing Two. Listing Three contains the JavaScript functions that are used to dynamically request data and update the contents of the page.
Conclusion
Ajax has helped redefine a technique that has been implemented for years, but has never been standardized. With more dynamic web applications appearing each day, combined with the increasing adoption of broadband Internet connectivity, the browser is being transformed into a rich desktop application. Ajax, and toolkits that support it, are turning the Web and its protocols into more of a dialog as opposed to a one-way, browser to server, conversation. Look for increasing integration of Ajax tools and techniques with popular web and application servers, such as Tomcat and WebSphere. It will be interesting to see what variants of the Ajax technique arise. However, the most important artifacts that will come from Ajax are the powerful, dynamic, web applications that transform our use of the Web.
DDJ
if (window.addEventListener) { window.addEventListener("load", init, false); } else if (window.attachEvent) { window.attachEvent("onload", init); } else { window.onload = init; } function init() { DWREngine.setErrorHandler(function(message) { alert(message); }); DWREngine.setWarningHandler(function(message) { alert(message); }); DWRUtil.useLoadingMessage(); update(); }Back to article
Listing Two
<p>Choose magazine: <select id="maglist" onclick="populateYearList();" style="vertical-align:top;"> </select></p> <p>Choose year: <select id="yearlist" onclick="populateMonthList();" style="vertical-align:top;"> </select></p> <p>Choose month: <select id="monthlist" onclick="populateArticleList();" style="vertical-align:top;"> </select></p> <p>Choose article to read: <select id="articlelist" onclick="displayArticle();" style="vertical-align:top;"> </select></p> <p>Article contents: <br><TEXTAREA name="articletext" id="articletext" rows="40" cols="81"> </TEXTAREA></p>Back to article
Listing Three
function populateYearList() { <!-- onYearData will be called with the data --> MagViewer.getPublicationYears(onYearData, maglist.value); } <!-- This is an Ajax callback method --> function onYearData(data) { DWRUtil.removeAllOptions("yearlist"); DWRUtil.addOptions("yearlist", data); } function populateMonthList() { <!-- onMonthData will be called with the data --> MagViewer.getYearTopics(onMonthData, maglist.value, yearlist.value); } <!-- This is an Ajax callback method --> function onMonthData(data) { DWRUtil.removeAllOptions("monthlist"); DWRUtil.addOptions("monthlist", data); } function populateArticleList() { <!-- onArticleData will be called with the data --> var month = 99; for ( var intLoop = 0; intLoop < monthlist.length; intLoop++) { if ( monthlist[intLoop].selected ) month = intLoop; } MagViewer.getIssueDetails(onArticleList, maglist.value, yearlist.value, month); } <!-- This is an Ajax callback method --> function onArticleList(data) { DWRUtil.removeAllOptions("articlelist"); DWRUtil.addOptions("articlelist", data); } function displayArticle() { var month = 99; for ( var intLoop = 0; intLoop < monthlist.length; intLoop++) { if ( monthlist[intLoop].selected ) month = intLoop; } var article = 99; for ( var intLoop = 0; intLoop < articlelist.length; intLoop++) { if ( articlelist[intLoop].selected ) article = intLoop; } MagViewer.getArticle(onArticleData, maglist.value, yearlist.value, month, article); } <!-- This is an Ajax callback method --> function onArticleData(data) { articletext.value = data; }Back to article