Ajax For Java Developers of IBM
Ajax For Java Developers of IBM
<!-- Table of products from store's catalog, one row per item --> <th>Name</th> <th>Description</th> <th>Price</th> <th></th> ... <tr> <!-- Item details --> <td>Hat</td> <td>Stylish bowler hat</td> <td>$19.99</td> <td> <!-- Click button to add item to cart via Ajax request --> <button onclick="addToCart('hat001')">Add to Cart</button> </td> </tr> ... <!-- Representation of shopping cart, updated asynchronously --> <ul id="cart-contents"> <!-- List-items will be added here for each item in the cart --> </ul> <!-- Total cost of items in cart displayed inside span element --> Total cost: <span id="total">$0.00</span>
Back to top
Now that you have a high-level view of the Ajax roundtrip, I'll zoom in for a more detailed look at each step along the way. Refer back to Figure 1 if you lose your place -- the sequence isn't entirely straightforward because of the asynchronous nature of the Ajax approach. Back to top
Dispatching an XMLHttpRequest
I'll start at the beginning of the Ajax sequence: creating and dispatching an XMLHttpRequest from the browser. Unfortunately, the method to create an XMLHttpRequest differs from browser to browser. The JavaScript function in Listing 2 smoothes out these browser-dependent wrinkles, detecting the correct approach for the current browser and returning an XMLHttpRequestready to use. It's best to think of this as boilerplate code: simply copy it into your JavaScript library and use it when you need anXMLHttpRequest.
Later, I'll discuss techniques to handle browsers that don't support XMLHttpRequest. For now, the examples assume that thenewXMLHttpRequest function from Listing 2 will always return an XMLHttpRequest instance. Getting back to the example shopping-cart scenario, I want to invoke an Ajax interaction whenever the user hits the Add to Cart button for a catalog item. The onclick handler function named addToCart() is responsible for updating the state of the cart through an Ajax call (see Listing 1). As shown in Listing 3, the first thing that addToCart() needs to do is obtain an instance ofXMLHttpRequest by calling the newXMLHttpRequest() function from Listing 2. Next, it registers a callback function to receive the server's response (I'll explain this in detail later; see Listing 6). Because the request will modify state on the server, I'll use an HTTP POST to do the deed. Sending data through POST requires three steps. First, I need to open a POST connection to the server resource I'm communicating with -- in this case a servlet mapped to the URL cart.do. Next, I set a
header on the XMLHttpRequest stating that the content of the request is form-encoded data. Finally, I send the request with form-encoded data as the body. Listing 3 puts these steps together. Listing 3. Dispatching an Add to Cart XMLHttpRequest
/* * Adds an item, identified by its product code, * to the shopping cart * itemCode - product code of the item to add. */ function addToCart(itemCode) { // Obtain an XMLHttpRequest instance var req = newXMLHttpRequest(); // Set the handler function to receive callback notifications // from the request object var handlerFunction = getReadyStateHandler(req, updateCart); req.onreadystatechange = handlerFunction; // Open an HTTP POST connection to the shopping cart servlet. // Third parameter specifies request is asynchronous. req.open("POST", "cart.do", true); // Specify that the body of the request contains form data req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); // Send form encoded data stating that I want to add the // specified item to the cart. req.send("action=add&item="+itemCode); }
And with that, you've seen the first part of setting up the Ajax roundtrip -- namely creating and dispatching the HTTP request from the client. Next up is the Java servlet code used to handle the request. Back to top
throws java.io.IOException { Cart cart = getCartFromSession(req); String action = req.getParameter("action"); String item = req.getParameter("item"); if ((action != null)&&(item != null)) { // Add or remove items from the Cart if ("add".equals(action)) { cart.addItem(item); } else if ("remove".equals(action)) { cart.removeItems(item); } } // Serialize the Cart's state to XML String cartXml = cart.toXml(); // Write XML to response. res.setContentType("application/xml"); res.getWriter().write(cartXml); }
Listing 5 shows an example of the XML produced by the Cart.toXml() method. It's pretty straightforward. Note the generatedattribute on the cart element, which is a timestamp produced by System.currentTimeMillis(). Listing 5. Example XML serialization of the Cart object
<?xml version="1.0"?> <cart generated="1123969988414" total="$171.95"> <item code="hat001"> <name>Hat</name> <quantity>2</quantity> </item> <item code="cha001"> <name>Chair</name> <quantity>1</quantity> </item> <item code="dog001"> <name>Dog</name> <quantity>1</quantity> </item> </cart>
If you take a look at Cart.java in the application source code available from the Download section, you'll see that the XML is produced simply by appending strings together. While sufficient for this example, this is pretty much the worst way to produce XML from Java code. I'll suggest some better approaches in the next installment of this series. So now you know how the CartServlet responds to an XMLHttpRequest. The next thing is to return to the client side, where you can see how the XML response is used to update page state. Back to top
HTTP status codes In Listing 6, the status property of XMLHttpRequest is tested to see if the request completed successfully.status contains the HTTP status code of the server's response. When performing simple GET and POSTrequests,
you can assume that any code other than 200 (OK) is an error. If the server sends a redirect response (for example, 301 or 302), the browser transparently follows the redirect and fetches the resource from the new location; the XMLHttpRequest doesn't see the redirect status code. Also, browsers automatically add a Cache-Control: no-cache header to allXMLHttpRequests, so client code will never have to deal with a 304 (not-modified) server response either.
About getReadyStateHandler()
reading JavaScript. The trade-off is that by including this function in your JavaScript library, you can simply handle Ajax server responses without having to deal with the internals of XMLHttpRequest. The important thing is that you understand how to use getReadyStateHandler() in your own code. In Listing 3, you saw getReadyStateHandler() called as follows:handlerFunction = getReadyStateHandler(req, updateCart). The function returned by getReadyStateHandler()in this case will check if the XMLHttpRequest in the variable reqhas completed and then call a function named updateCart with the response XML.
And with that, the whistle-stop tour of the Ajax roundtrip is complete, although you may want to get the Web app running and see it in action (see the Download section). The example is very simple, with plenty of scope for improvement. For instance, I've included server-side code to remove items from the cart, but no way to access it from the UI. For a good exercise, try building on the application's existing JavaScript code to implement this functionality. Back to top
Availability of XMLHttpRequest
One of the biggest issues facing Ajax developers is how to respond when XMLHttpRequest isn't available. While the majority of modern browsers support XMLHttpRequest, there will always be a minority of users whose browsers do not, or whose browser security settings prevent XMLHttpRequest from being used. If you're developing a Web app to be deployed on a corporate intranet, you probably have the luxury of specifying which browsers are supported and assuming XMLHttpRequest is always available. If you're deploying on the public Web, however, you must be aware that by presuming XMLHttpRequest is available, you are potentially preventing users of older browsers, browsers for people with disabilities, or lightweight browsers on handheld devices from using your application. Therefore, you should endeavor to make your application "degrade gracefully" and remain functional in browsers withoutXMLHttpRequest support. In the shopping-cart example, the best way to degrade the application would be to have the Add to Cart buttons perform a regular form submission, refreshing the page to reflect the cart's updated status. The Ajax behavior could be added to the page through JavaScript once the page was loaded, attaching a JavaScript handler function to each Add to Cart button only if XMLHttpRequest was available. Another approach would be to detect XMLHttpRequest when a user logged in, and then serve up either an Ajax version of the application or a regular forms-based version as appropriate.
Usability concerns
Some of the usability issues surrounding Ajax applications are more general. For instance, it can be important to let users know that their input has been registered, because the usual feedback mechanisms of the hourglass cursor and spinning browser "throbber" do not apply to XMLHttpRequests. One technique is to replace Submit buttons with a "Now updating..." type message so that users do not repeatedly click on buttons while waiting for a response. Another issue is that users may fail to notice that parts of the page they're viewing have been updated. You can alleviate this problem by using a variety of visual techniques to subtly draw the user's eye to updated areas of the page. Other issues caused by updating the page with Ajax include "breaking" the browser's back button, and the URL in the address bar not reflecting the entire state of the page, preventing bookmarking. See the Resources section for articles that specifically address the usability issues of Ajax applications.
Server load
Implementing an Ajax UI in place of a regular forms-based one may dramatically increase the number of requests made to the server. For instance, a regular Google Web search causes one hit on the server, occurring when the user submits the search form. However, Google Suggest, which attempts to autocomplete your search terms, sends several requests to the server as the user types. When developing an Ajax application, be aware of how many requests you'll be sending to the server and the resulting server load this will cause. You can mitigate server load by buffering requests on the client and caching server responses in the client, where applicable. You should also
attempt to design your Ajax Web applications so that as much logic as possible can be performed on the client, without needing to contact the server.
In conclusion
You should now have a good understanding of the fundamental principles of Ajax and a nuts-andbolts knowledge of the client- and server-side components that participate in an Ajax interaction. These are the building blocks of a Java-based Ajax Web application. In addition, you should understand some of the high-level design issues that come with the Ajax approach. Creating a successful Ajax application requires a holistic approach -- from UI design through JavaScript design to server-side architecture -- but you should now be armed with the core Ajax knowledge needed to consider these other aspects. There's good news if you're feeling daunted by the complexity of writing a large Ajax application using the techniques demonstrated here. Just as frameworks like Struts, Spring, and Hibernate have evolved to abstract Web application development away from the low-level details of the Servlet API and JDBC, so toolkits are appearing to ease Ajax development. Some of these focus solely on the client side, providing easy ways to add visual effects to your pages or streamlining the use ofXMLHttpRequest. Others go further, providing means to automatically generate Ajax interfaces from server-side code. These frameworks do the heavy lifting for you, so that you can take a more high-level approach to Ajax development. I'll be looking at some of them in this series. The Ajax community is fast moving, and there's a great deal of valuable information out there. Before reading the next installment in this series, I recommend that you consult the articles listed in the Resources section, especially if you're new to Ajax or client-side development. You should also take some time to study the example source code and think about ways to improve it. In the next article in this series, I'll discuss the XMLHttpRequest API in more detail and suggest ways to create XML easily from your JavaBeans. I'll also show you alternatives to XML for Ajax data transfer, such as the JSON (JavaScript Object Notation) lightweight data-interchange format.