Anyone who has used Flickr, GMail, Google Suggest, or Google Maps will realize that a new breed of dynamic web applications is emerging. These applications look and act very similar to traditional desktop applications without relying on plug-ins or browser-specific features. Web applications have traditionally been a set of HTML pages that must be reloaded to change any portion of the content. Technologies such as JavaScript programming language and cascading style sheets (CSS) have matured to the point where they can be used effectively to create very dynamic web applications that will work on all of the major browsers. This article will detail several techniques that you can use today to enable your web applications to be more rich and interactive like desktop applications.
Introducing Asynchronous JavaScript Technology and XML (AJAX)
Using JavaScript technology, an HTML page can asynchronously make calls to the server from which it was loaded and fetch XML documents. The XML documents may then be used by the JavaScript technology to update or modify the Document Object Model (DOM) of the HTML page. The term Asynchronous JavaScript Technology and XML (AJAX) has emerged recently to describe this interaction model. AJAX is not new. These techniques have been available to developers targeting Internet Explorer on the Windows platform for many years. Until recently, the technology was known as web remoting or remote scripting. Web developers have also used a combination of plug-ins, Java applets, and hidden frames to emulate this interaction model for some time. What has changed recently is that the inclusion of support for the XMLHttpRequest object has became ubiquitous in the mainstream browsers across all platforms. The real magic is the result of the JavaScript technology's XMLHttpRequest object. Although this object is not specified in the formal JavaScript technology specification, all of today's mainstream browsers support it. The subtle differences with the JavaScript technology and CSS support among current generation browsers such as Firefox, Internet Explorer, and Safari are manageable. If you are required to support older browsers, AJAX may not be the answer for you. What makes AJAX-based clients unique is that the client contains page-specific control logic embedded as JavaScript technology. The page interacts with the JavaScript technology based on events such as the document being loaded, a mouse click, focus changes, or even a timer. AJAX interactions allow for a clear separation of presentation logic from the data. An HTML page can pull in bite-size pieces of data as needed rather than reloading the whole page every time a change needs to be displayed. AJAX will require a different server-side architecture to support this interaction model. Traditionally, server-side web applications have focused on generating HTML documents for every client event resulting in a call to the server. The clients would then refresh and re-render the complete HTML page for each response. Rich web applications focus on a client fetching an HTML document that acts as a template or container into which to inject content, based on client events using XML data retrieved from a server-side component. Some uses for AJAX interactions are the following:
- Real-Time Form Data Validation: Form data such as user IDs, serial numbers, postal codes, or even special coupon codes that require server-side validation can be validated in a form before the user submits a form.
- Autocompletion: A specific portion of form data such as an email address, name, or city name may be autocompleted as the user types.
- Master Details Operations: Based on a client event, an HTML page can fetch more detailed information on data such as a product listing that enables the client to view the individual product information without refreshing the page.
- Sophisticated User Interface Controls: Controls such as tree controls, menus, and progress bars may be provided that do not require page refreshes.
- Refreshing Data on the Page: HTML pages may poll data from a server for up-to-date data such as scores, stock quotes, weather, or application-specific data.
- Server-side Notifications: An HTML page may simulate a server-side push by polling the server for event notifications that may notify the client with a message, refresh page data, or redirect the client to another page.
This list is not all-inclusive, but it shows that AJAX interactions allow web applications to do much more than they have done in the past. Although many of these benefits are noteworthy, this approach has some drawbacks as well:
- Complexity: Server-side developers will need to understand that presentation logic will be required in the HTML client pages as well as in the server-side logic to generate the XML content needed by the client HTML pages. HTML page developers must have JavaScript technology skills. Creating AJAX-enabled applications will become easier as new frameworks are created and existing frameworks evolve to support the interaction model.
- Standardization of the
XMLHttpRequest Object: The XMLHttpRequest object is not yet part of the JavaScript technology specification, which means that the behavior may vary depending on the client. - JavaScript Technology Implementations: AJAX interactions depend heavily on JavaScript technology, which has subtle differences depending on the client. See QuirksMode.org for more details on browser-specific differences.
- Debugging: AJAX applications are also difficult to debug because the processing logic is embedded both in the client and on the server.
- Viewable Source: The client-side JavaScript technology may be viewed simply by selecting View Source from an AJAX-enabled HTML page. A poorly designed AJAX-based application could open itself up to hackers or plagiarism.
Frameworks and patterns for AJAX technology are likely to emerge as developers gain more experience writing applications that use the AJAX interaction model. It is still early to focus on a one-size-fits-all framework for AJAX interactions. This article and the associated solutions focus on how AJAX interactions can be supported today by existing Java 2 Platform, Enterprise Edition (J2EE) technologies such as servlets, JavaServer Pages (JSP) software, JavaServer Faces applications, and the Java Standard Tag Libraries (JSTL).
The Anatomy of an AJAX Interaction
Now that we have discussed what AJAX is and what some higher-level issues are, let's put all the pieces together and show an AJAX- enabled J2EE application. Let's consider an example. A web application contains a static HTML page, or an HTML page generated in JSP technology contains an HTML form that requires server-side logic to validate form data without refreshing the page. A server-side web component (servlet) named ValidateServlet will provide the validation logic. Figure 1 describes the details of the AJAX interaction that will provide the validation logic.
|
Figure 1: An AJAX Interaction Provides Validation Logic
|
The following items represent the setups of an AJAX interaction as they appear in Figure 1.
- A client event occurs.
- An
XMLHttpRequest object is created and configured. - The
XMLHttpRequest object makes a call. - The request is processed by the
ValidateServlet . - The
ValidateServlet returns an XML document containing the result. - The
XMLHttpRequest object calls the callback() function and processes the result. - The HTML DOM is updated.
Now let's look at each step of the AJAX interaction in more detail. 1. A client event occurs. JavaScript technology functions are called as the result of an event. In this case, the function validate() may be mapped to a onkeyup event on a link or form component.
<input type="text"
size="20"
id="userid"
name="id"
οnkeyup="validate();"> |
This form element will call the validate() function each time the user presses a key in the form field. 2. A XMLHttpRequest object is created and configured. An XMLHttpRequest object is created and configured.
var req;
function validate() {
var idField = document.getElementById("idField");
var url = "validate?id=" + escape(idField.value);
if (window.XMLHttpRequest) {
req = new XMLHttpRequest();
} else if (window.ActiveXObject) {
req = new ActiveXObject("Microsoft.XMLHTTP");
}
req.open("GET", url, true);
req.onreadystatechange = callback;
req.send(null);
} |
The validate() function creates an XMLHttpRequest object and calls the open function on the object. The open function requires three arguments: the HTTP method, which is GET or POST ; the URL of the server-side component that the object will interact with; and a boolean indicating whether or not the call will be made asynchronously. The API is XMLHttpRequest.open(String method, String URL, boolean asynchronous) . If an interaction is set as asynchronous (true ) a callback function must be specified. The callback function for this interaction is set with the statement req.onreadystatechange = callback; . See section 6 for more details. 3. The XMLHttpRequest object makes a call. When the statement req.send(null); is reached, the call will be made. In the case of an HTTP GET , this content may be null or left blank. When this function is called on the XMLHttpRequest object, the call to the URL that was set during the configuration of the object is called. In the case of this example, the data that is posted (id ) is included as a URL parameter. Use an HTTP GET when the request is idempotent, meaning that two duplicate requests will return the same results. When using the HTTP GET method, the length of URL, including escaped URL parameters, is limited by some browsers and by server-side web containers. The HTTP POST method should be used when sending data to the server that will affect the server-side application state. An HTTP POST requires a Content-Type header to be set on the XMLHttpRequest object by using the following statement:
req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
req.send("id=" + escape(idTextField.value));
|
When sending form values from JavaScript technology, you should take into consideration the encoding of the field values. JavaScript technology includes an escape() function that should be used to ensure that localized content is encoded properly and that special characters are escaped correctly. 4. The request is processed by the ValidateServlet . A servlet mapped to the URI "validate" checks whether the user ID is in the user database. A servlet processes an XMLHttpRequest just as it would any other HTTP request. The following example show a server extracting the id parameter from the request and validating whether the parameter has been taken.
public class ValidateServlet extends HttpServlet {
private ServletContext context;
private HashMap users = new HashMap();
public void init(ServletConfig config) throws ServletException {
this.context = config.getServletContext();
users.put("greg","account data");
users.put("duke","account data");
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
String targetId = request.getParameter("id");
if ((targetId != null) && !users.containsKey(targetId.trim())) {
response.setContentType("text/xml");
response.setHeader("Cache-Control", "no-cache");
response.getWriter().write("
valid");
} else {
response.setContentType("text/xml");
response.setHeader("Cache-Control", "no-cache");
response.getWriter().write("
invalid");
}
}
}
|
In this example, a simple HashMap is used to contain the users. In the case of this example, let us assume that the user typed duke as the ID. 5. The ValidateServlet returns an XML document containing the results. The user ID "duke" is present in the list of user IDs in the users HashMap . The ValidateServlet will write an XML document to the response containing a message element with the value of invalid . More complex usecases may require DOM, XSLT, or other APIs to generate the response.
response.setContentType("text/xml");
response.setHeader("Cache-Control", "no-cache");
response.getWriter().write("
invalid");
|
The developer must be aware of two things. First, the Content-Type must be set to text/xml . Second, the Cache-Control must be set to no-cache . The XMLHttpRequest object will process only requests that are of the Content-Type of only text/xml , and setting Cache-Control to no- cache will keep browsers from locally caching responses for cases in which duplicate requests for the same URL (including URL parameters) may return different responses. 6. The XMLHttpRequest object calls the callback() function and processes the result. 7. The HTML DOM is updated. The XMLHttpRequest object was configured to call the callback() function when there are changes to the readyState of the XMLHttpRequest object. Let us assume the call to the ValidateServlet was made and the readyState is 4 , signifying the XMLHttpRequest call is complete. The HTTP status code of 200 signifies a successful HTTP interaction.
function callback() {
if (req.readyState == 4) {
if (req.status == 200) {
// update the HTML DOM based on whether or not message is valid
}
}
}
|
Browsers maintain an object representation of the documents being displayed (referred to as the Document Object Model or DOM). JavaScript technology in an HTML page has access to the DOM, and APIs are available that allow JavaScript technology to modify the DOM after the page has loaded. Following a successful request, JavaScript technology code may modify the DOM of the HTML page. The object representation of the XML document that was retrieved from the ValidateServlet is available to JavaScript technology code using the req.responseXML , where req is an XMLHttpRequest object. The DOM APIs provide a means for JavaScript technology to navigate the content from that document and use that content to modify the DOM of the HTML page. The string representation of the XML document that was returned may be accessed by calling req.responseText . Now let's look at how to use the DOM APIs in JavaScript technology by looking at the following XML document returned from the ValidateServlet .
<message>
valid
</message>
|
This example is a simple XML fragment that contains the sender of the message element, which is simply the string valid or invalid . A more advanced sample may contain more than one message and valid names that might be presented to the user:
function parseMessage() {
var message = req.responseXML.getElementsByTagName("message")[0];
setMessage(message.childNodes[0].nodeValue);
}
|
The parseMessages() function will process an XML document retrieved from the ValidateServlet . This function will call the setMessage() with the value of the message element to update the HTML DOM. 7. The HTML DOM is updated. JavaScript technology can gain a reference to any element in the HTML DOM using a number of APIs. The recommended way to gain a reference to an element is to call document.getElementById("userIdMessage") , where "userIdMessage" is the ID attribute of an element appearing in the HTML document. With a reference to the element, JavaScript technology may now be used to modify the element's attributes; modify the element's style properties; or add, remove, or modify child elements. One common means to change the body content of an element is to set the innerHTML property on the element as in the following example.
<script type="text/javascript">
function setMessage(message) {
var userMessageElement = document.getElementById("userIdMessage");
userMessageElement.innerHTML = "<font color=/"red/">" + message + " </font>";
}
</script>
<body>
<div id="userIdMessage"></div>
</body>
|
The portions of the HTML page that were affected are re-rendered immediately following the setting of the innerHTML . If the innerHTML property contains elements such as <image> or <iframe> , the content specified by those elements is fetched and rendered as well. The main drawback with this approach is that HTML elements are hardcoded as strings in the JavaScript technology code. Hardcoding HTML markup inside JavaScript technology code is not a good practice because it makes the code difficult to read, maintain, and modify. Consider using the JavaScript technology DOM APIs to create or modify HTML elements within JavaScript technology code. Intermixing presentation with JavaScript technology code as strings will make a page difficult to read and edit. Another means of modifying the HTML DOM is to dynamically create new elements and append them as children to a target element as in the following example.
<script type="text/javascript">
function setMessage(message) {
var userMessageElement = document.getElementById("userIdMessage");
var userIdMessageFont = document.getElementById("userIdMessageFont");
var messageElement = document.createTextNode(message);
if (userMessageElement.childNodes[0]) {
// update the elements
userIdMessageFont.replaceChild(messageElement, userIdMessageFont.childNodes[0]);
} else {
// create the new elements
var fontElement = document.createTextNode("font");
fontElement.setAtribute("id", "userIdMessageFont");
fontElement.setAtribute("color", "red");
userMessageElement.appendChild(fontElement);
fontElement.appendChild(messageElement);
}
}
</script>
<body>
<div id="userIdMessage"></div>
</body>
|
The code sample shows how JavaScript technology DOM APIs may be used to create an element or alter the element programmatically. The support for JavaScript technology DOM APIs can differ in various browsers, so you must take care when developing applications.
The Java BluePrints Solutions Catalog
The Java Blueprints Solutions Catalog is being used to collect best practices of AJAX with J2EE technologies. The catalog addresses the best practices and blueprints for building applications using J2EE. Each solution includes a problem and solution statement, a design document, and working source code. These solutions are meant to be reused or included in your applications as you see the need. Following are a list of solutions provided for AJAX interactions.
Autocomplete
Autocomplete provides a simplified means of data navigation as the user enters a request in an HTML form. When faced with a large set of data, the user can be presented with a list of potential completions as the data is entered. Selecting one of the completions ensures that the user is entering data that exists on the server. Consider a name finder web application inside a large corporation. Details about an individual can be obtained just from entering the first few characters of the first or last name, as in Figure 2. The user can then navigate to the user in question with one click.
|
Figure 2: Autocompletion of a Name
|
Progress Bar
In web applications, a server-side task may take some time to complete. This time may extend longer than the time out of an HTTP interaction. Frustrated users may resubmit a form or quit a session when they do not know how long the task will take. Typically, web applications use page refreshes to track the progress of a server-side operation, which can be distracting and not accurate. AJAX can be used to track the progress of a server-side operation in an HTML page without refreshing the HTML page. The user will see the progress of the server-side operation graphically in Figure 3.
|
Figure 3: Progress Bar
|
Refreshing Data
Providing up-to-date data or server notifications to an HTML page is important in the web world, where data is constantly changing. Although this is not push technology in a literal sense, it is simulated by polling from the client using AJAX interactions. When data needs to be updated or notifications need to be made, the HTML page is dynamically changed. Figure 4 shows a server-side counter being displayed in an HTML page. The counter will update in the background of the page.
|
Figure 4: Server-side Counter Shows Refreshing Data
|
Real-Time Validation
Not all form fields can be validated on the client using JavaScript technology alone. Some types of form data require server-side validation logic. Conventional web applications have used page refreshes to do this type of validation, but it can be a little distracting. Consider a web application that requires a unique user ID. Using AJAX interactions, the user can know while typing whether or not the ID is valid (Figure 5).
|
Figure 5: Invalidating the ID as User Types
|
When a user enters an invalid user ID, the application disables the submit button and presents a message to the user (Figure 6).
|
Figure 6: Validating the ID as User Types
|
The user knows right away that the user ID is available and valid.
Final Thoughts
We have seen that AJAX interactions can solve many problems. J2EE technology provides a good base to develop and deploy AJAX-based applications with APIs for tying in HTTP processing, databases, web services, XML processing, and business objects. With a better understanding of this interaction model, today's applications can become more interactive, providing the end user with a better experience. Using AJAX requires that you use the latest browser versions that support the XMLHttpRequest object needed for AJAX interactions. Using AJAX also requires a great deal of client-side JavaScript technology and CSS. As an application architect or developer, you will need to weigh the needs of having a rich application against browser support, architecture complexity, and developer training. As the AJAX programming model evolves, existing technologies and frameworks will make this transition easier. What is evident is that prominent web applications are increasingly becoming more interactive. Are yours?
For More Information
Greg Murray is a Sun Microsystems engineer, the servlet specification lead, and a former member of the BluePrints team, where he focused on the web tier. He is coauthor of Designing Enterprise Applications With the Java 2 Platform, Enterprise Edition and Designing Web Services With the J2EE 1.4 Platform (Addison-Wesley). The XMLHttpRequest object was configured to call the callback() function when there are changes to the readyState of the XMLHttpRequest object. Let us assume the call to the ValidateServlet was made and the readyState is 4 , signifying the XMLHttpRequest call is complete. The HTTP status code of 200 signifies a successful HTTP interaction.
function callback() {
if (req.readyState == 4) {
if (req.status == 200) {
// update the HTML DOM based on whether or not message is valid
}
}
}
|
Browsers maintain an object representation of the documents being displayed (referred to as the Document Object Model or DOM). JavaScript technology in an HTML page has access to the DOM, and APIs are available that allow JavaScript technology to modify the DOM after the page has loaded. Following a successful request, JavaScript technology code may modify the DOM of the HTML page. The object representation of the XML document that was retrieved from the ValidateServlet is available to JavaScript technology code using the req.responseXML , where req is an XMLHttpRequest object. The DOM APIs provide a means for JavaScript technology to navigate the content from that document and use that content to modify the DOM of the HTML page. The string representation of the XML document that was returned may be accessed by calling req.responseText . Now let's look at how to use the DOM APIs in JavaScript technology by looking at the following XML document returned from the ValidateServlet .
<message>
valid
</message>
|
This example is a simple XML fragment that contains the sender of the message element, which is simply the string valid or invalid . A more advanced sample may contain more than one message and valid names that might be presented to the user:
function parseMessage() {
var message = req.responseXML.getElementsByTagName("message")[0];
setMessage(message.childNodes[0].nodeValue);
}
|
The parseMessages() function will process an XML document retrieved from the ValidateServlet . This function will call the setMessage() with the value of the message element to update the HTML DOM. |