Tag Archives: Ajax-RPC

[repost ] Ajax for Java developers: Ajax with Direct Web Remoting

Summary: Exciting as it is, adding Ajax functionality to your applications can mean a lot of hard work. In this third article in the Ajax for Java™ developers series, Philip McCarthy shows you how to use Direct Web Remoting (DWR) to expose JavaBeans methods directly to your JavaScript code and automate the heavy-lifting of Ajax.

View more content in this series

Understanding the fundamentals of Ajax programming is essential, but if you’re building complex Ajax UIs, it’s also important to be able to work at a higher level of abstraction. In this third article in the Ajax for Java developers series, I build on last month’s introduction to data serialization techniques for Ajax, introducing a technique that will let you avoid the nitty-gritty details of serializing Java objects.

In the previous article, I showed you how to use JavaScript Object Notation (JSON) to serialize data in a format easily converted into JavaScript objects on the client. With this setup, you can invoke remote service calls using JavaScript code and receive JavaScript object graphs in response, not unlike making a remote procedure call. This time, you learn how to take things one step further, using a framework that formalizes your ability to make remote procedure calls on server-side Java objects from JavaScript client code.

DWR is an open source, Apache licensed solution consisting of server-side Java libraries, a DWR servlet, and JavaScript libraries. While DWR is not the only Ajax-RPC toolkit available for the Java platform, it is one of the most mature, and it offers a great deal of useful functionality. See Resources to download DWR before proceeding with the examples.

What is DWR?

In the simplest terms, DWR is an engine that exposes methods of server-side Java objects to JavaScript code. Effectively, with DWR, you can eliminate all of the machinery of the Ajax request-response cycle from your application code. This means your client-side code never has to deal with an XMLHttpRequest object directly, or with the server’s response. You don’t need to write object serialization code or use third-party tools to turn your objects into XML. You don’t even need to write servlet code to mediate Ajax requests into calls on your Java domain objects.

DWR is deployed as a servlet within your Web application. Viewed as a black box, this servlet performs two major roles: First, for each exposed class, DWR dynamically generates JavaScript to include in your Web page. The generated JavaScript contains stub functions that represent the corresponding methods on the Java class and also performs XMLHttpRequests behind the scenes. These requests are sent to the DWR servlet, which, in its second role, translates the request into a method call on a server-side Java object and sends the method’s return value back to the client side in its servlet response, encoded into JavaScript. DWR also provides JavaScript utility functions that help perform common UI tasks.

Back to top

About the example

Before explaining DWR in more detail, I’ll introduce a simple example scenario. As in the previous articles, I’ll use a minimal model based on an online store, this time consisting of a basic product representation, a user’s shopping cart that can contain product items, and a data access object (DAO) to look up product details from a data store. The Item class is the same one used in the previous article, but it no longer implements any manual serialization methods. Figure 1 depicts this simple setup:
Figure 1. Class diagram depicting the Cart, CatalogDAO, and Item classes

I’ll demonstrate two very simple use cases within this scenario. First, the user can perform a text search on the catalog and see matching items. Second, the user can add items to the shopping cart and see the total cost of items in the cart.

Back to top

Implementing the catalog

The starting point of a DWR application is writing your server-side object model. In this case, I start by writing a DAO to provide search capabilities on the product catalog datastore. CatalogDAO.java is a simple, stateless class with a zero-argument constructor. Listing 1 shows the signatures of the Java methods that I want to expose to Ajax clients:
Listing 1. The CatalogDAO methods to expose via DWR

/**
 * Returns a list of items in the catalog that have 
 *  names or descriptions matching the search expression
 * @param expression Text to search for in item names 
 *  and descriptions 
 * @return list of all matching items
 */
public List<Item> findItems(String expression);

/**
 * Returns the Item corresponding to a given Item ID
 * @param id The ID code of the item
 * @return the matching Item
 */
public Item getItem(String id);

 

Next, I need to configure DWR, telling it that Ajax clients should be able to construct a CatalogDAO and call these methods. I do this with the dwr.xml config file shown in Listing 2:
Listing 2. Config to expose CatalogDAO methods

<!DOCTYPE dwr PUBLIC
  "-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN"
  "http://www.getahead.ltd.uk/dwr/dwr10.dtd">
<dwr>
  <allow>
    <create creator="new" javascript="catalog">
      <param name="class" 
        value="developerworks.ajax.store.CatalogDAO"/>
      <include method="getItem"/> 
      <include method="findItems"/> 
    </create> 
    <convert converter="bean" 
      match="developerworks.ajax.store.Item">
      <param name="include" 
        value="id,name,description,formattedPrice"/>
    </convert>
  </allow>
</dwr>

 

The root element of the dwr.xml document is dwr. Inside this element is the allow element, which specifies the classes that DWR will remote. The two child elements of allow are create and convert.

The create element

The create element tells DWR that a server-side class should be exposed to Ajax requests and defines how DWR should obtain an instance of that class to remote. The creator attribute here is set to the value new, meaning that DWR should call the class’s default constructor to obtain an instance. Other possibilities are to create an instance through a fragment of script using the Bean Scripting Framework (BSF), or to obtain an instance via integration with the IOC container, Spring. By default, when an Ajax request to DWR invokes a creator, the instantiated object is placed in page scope and therefore is no longer available after the request completes. In the case of the stateless CatalogDAO, this is fine.

The javascript attribute of create specifies the name by which the object will be accessible from JavaScript code. Nested within the create element, a param element specifies the Java class that the creator will create. Finally, include elements specify the names of the methods that should be exposed. Explicitly stating the methods to expose is good practice to avoid accidentally allowing access to potentially harmful functionality — if this element is omitted, all of the class’s methods will be exposed to remote calls. Alternately, you can use exclude elements to specify only those methods you wish to prevent access to.

The convert element

While creators are concerned with exposing classes and their methods for Web remoting, convertors are concerned with the parameters and return types of those methods. The role of the convert element is to tell DWR how to convert datatypes between their server-side Java object representation and serialized JavaScript representation, and vice versa.

DWR automatically mediates simple data types between Java and JavaScript representations. These types include Java primitives, along with their respective class representations, as well as Strings and Dates, arrays, and collection types. DWR can also convert JavaBeans into JavaScript representations, but for security reasons, doing so requires explicit configuration.

The convert element in Listing 2 tells DWR to use its reflection-based bean convertor for the Items returned by the exposed methods of CatalogDAO and specifies which of the Item‘s members should be included in the serialization. The members are specified using the JavaBean naming convention, so DWR will call the corresponding get methods. In this case, I’m omitting the numerical price field and instead including the formattedPrice field, which is currency-formatted ready for display.

At this point, I’m ready to deploy my dwr.xml to my Web application’s WEB-INF directory, where the DWR servlet will pick it up. Before proceeding, however, it’s a good idea to ensure that everything is working as expected.

Back to top

Testing the deployment

If the DWRServlet‘s web.xml definition sets the init-param debug to true, then DWR’s extremely helpful test mode is enabled. Navigating to /{your-web-app}/dwr/ brings up a list of your classes that DWR has been configured to remote. Clicking through takes you to the status screen for a given class. The DWR test page for CatalogDAO is shown in Figure 2. As well as providing a script tag to paste into your Web pages, pointing to DWR’s generated JavaScript for the class, this screen also provides a list of the class’s methods. The list includes methods inherited from the class’s supertypes, but only those methods that I explicitly specified for remoting in dwr.xml are marked as accessible.
Figure 2. DWR test page for CatalogDAO
The diagnostic and test page generated by DWR for CatalogDAO

It’s possible to enter parameter values into the textboxes next to the accessible methods and hit the Execute button to invoke them. The server’s response will be displayed using JSON notation in an alert box, unless it is a simple value, in which case it will be displayed inline alongside the method. These test pages are very useful. Not only do they allow you to easily check which classes and methods are exposed for remoting, you can also test that each method is behaving as expected.

Once you’re satisfied that your remoted methods are working correctly, you can use DWR’s generated JavaScript stubs to call on your server-side objects from client-side code.

Back to top

Calling a remoted object

The mapping between remoted Java object methods and their corresponding JavaScript stub functions is simple. The general form is JavaScriptName.methodName(methodParams ..., callBack), where JavaScriptName is whatever name was specified as the creator‘s javascript attribute, methodParams represents the Java method’s n parameters, and callback is a JavaScript function that will be called with the Java method’s return value. If you’re familiar with Ajax, you’ll recognize the callback mechanism as the usual approach to XMLHttpRequest‘s asynchrony.

In the example scenario, I use the JavaScript functions in Listing 3 to perform a search and update the UI with the search results. This listing also uses convenience functions from DWR’s util.js. Of particular note is the JavaScript function named $(), which can be thought of as a souped-up version of document.getElementById(). It’s certainly easier to type. If you’ve used the prototype JavaScript library, you’ll be familiar with this function.
Listing 3. Calling the remoted findItems() method from the client

/*
 * Handles submission of the search form
 */
function searchFormSubmitHandler() {

  // Obtain the search expression from the search field
  var searchexp = $("searchbox").value;

  // Call remoted DAO method, and specify callback function
  catalog.findItems(searchexp, displayItems);

  // Return false to suppress form submission
  return false;
}

/*
 * Displays a list of catalog items
 */
function displayItems(items) {

  // Remove the currently displayed search results
  DWRUtil.removeAllRows("items");

  if (items.length == 0) {
    alert("No matching products found");
    $("catalog").style.visibility = "hidden";
  } else {

    DWRUtil.addRows("items",items,cellFunctions);
    $("catalog").style.visibility = "visible";
  }
}

 

In the above searchFormSubmitHandler() function, the code of interest is of course catalog.findItems(searchexp, displayItems);. This single line of code is all that is needed to send an XMLHttpRequest over the network to the DWR servlet and call the displayItems() function with the remoted object’s response.

The displayItems() callback itself is invoked with an array of Item representations. This array is passed to theDWRUtil.addRows() convenience function, along with the ID of a table to populate and an array of functions. There are as many functions in this array as there are cells in each table row. Each function is called in turn with an Item from the array and should return the content with which to populate the corresponding cell.

In this case, I want each row in the table of items to display the item’s name, description, and price, as well as an Add to Cartbutton for the item in the last column. Listing 4 shows the cell functions array that accomplishes this:
Listing 4. Cell functions array to populate the items table

/*
 * Array of functions to populate a row of the items table
 * using DWRUtil's addRows function
 */
var cellFunctions = [
  function(item) { return item.name; },
  function(item) { return item.description; },
  function(item) { return item.formattedPrice; },
  function(item) {
    var btn = document.createElement("button");
    btn.innerHTML = "Add to cart";
    btn.itemId = item.id;
    btn.onclick = addToCartButtonHandler;
    return btn;
  }
];

 

The first three of these functions simply return the content of the fields included in Item‘s convertor in dwr.xml. The last function creates a button, attaches the Item‘s ID to it, and specifies that a function named addToCartButtonHandler should be called when the button is clicked. This function is the entry point for the second use case: adding an Item to the shopping cart.

Back to top

Implementing the shopping cart

Security in DWR

DWR has been designed with security in mind. Using dwr.xml to whitelist only those classes and methods you wish to remote avoids accidental exposure of functionality that could be maliciously exploited. In addition to this, using the debug Test Mode, it is easy to audit all of the classes and methods that are exposed to the Web.

DWR also supports role-based security. You can specify the J2EE role that a user must be in to access a particular bean, via its creator configuration. By deploying multiple URL-secured instances of theDWRServlet, each with its own dwr.xml config file, you can also provide different sets of users with different remoted functionality.

The Java representation of the user’s shopping cart is based on aMap. When an Item is added to the cart, the Item itself is inserted into the Map as the key. The corresponding value in the Map is anInteger representing the quantity of the given Item in the cart. Thus the Cart.java has a field, contents, declared asMap<Item,Integer>.

Using complex types as hash keys presents a problem to DWR — in JavaScript, array keys must be literals. As a result, the contents Mapcannot be converted by DWR as it is. However, for the purposes of the shopping cart UI, all the user needs to see is the name and quantity of each item in the cart. So I can add a method namedgetSimpleContents() to Cart, which takes the contents Map and builds a simplified Map<String,Integer> from it, representing only the name and quantity of each Item. This string-keyed map representation can simply be converted into JavaScript by DWR’s built-in convertors.

The other field of Cart that the client is interested in is thetotalPrice, representing the sum total of everything in the shopping cart. As with Item, I’ve provided a synthetic member named formattedTotalPrice that is a pre-formatted Stringrepresentation of the numerical total.

Converting the Cart

Instead of having the client code make two calls on Cart, one to obtain the contents and one for the total price, I want to send all of this data to the client at once. To accomplish this I’ve added the strange-looking method, shown in Listing 5:
Listing 5. Cart.getCart() method

/**
 * Returns the cart itself - for DWR
 * @return the cart
 */ 
public Cart getCart() {
  return this;
}

 

While this method would be completely redundant in normal Java code (because you already have a reference to the Cart if you’re calling the method), it allows a DWR client to have Cart serialize itself to JavaScript.

Aside from getCart(), the other method that needs to be remoted is addItemToCart(). This method takes a Stringrepresentation of a catalog Item’s ID, adds that item to the Cart, and updates the price total. The method also returns the Cart, so that the client code can update the Cart contents and receive its new state in a single operation.

Listing 6 is the extended dwr.xml config file that includes the extra config info for remoting the Cart class:
Listing 6. Modified dwr.xml incorporating the Cart class

<!DOCTYPE dwr PUBLIC
    "-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN"
    "http://www.getahead.ltd.uk/dwr/dwr10.dtd">
<dwr>
  <allow>
    <create creator="new" javascript="catalog">
      <param name="class" 
        value="developerworks.ajax.store.CatalogDAO"/>
      <include method="getItem"/>
      <include method="findItems"/>
    </create>
    <convert converter="bean" 
      match="developerworks.ajax.store.Item">
      <param name="include" 
        value="id,name,description,formattedPrice"/>
    </convert>
    <create creator="new" scope="session" javascript="Cart">
      <param name="class" 
        value="developerworks.ajax.store.Cart"/>
      <include method="addItemToCart"/>
      <include method="getCart"/>
    </create>
    <convert converter="bean" 
      match="developerworks.ajax.store.Cart">
      <param name="include" 
        value="simpleContents,formattedTotalPrice"/>
    </convert>
  </allow>
</dwr>

 

In this version of dwr.xml, I’ve added both a creator and a convertor for Cart. The create element specifies that theaddItemToCart() and getCart() methods should be remoted and, importantly, that the created Cart instance should be placed in the user’s session. As a result, the content of the cart will persist between user requests.

The convert element for Cart is necessary because the remoted Cart methods return the Cart itself. Here I’ve specified that the members of Cart that should be present in its serialized JavaScript form are the map simpleContents and the StringformattedTotalPrice.

If you find this slightly confusing, remember that the create element specifies the server-side methods on Cart that can be called by a DWR client, and the convert element specifies the members to include in the JavaScript serialization of Cart.

Now I can implement the client-side code that calls my remoted Cart methods.

Back to top

Calling remoted Cart methods

First of all, when the store Web page first loads, I want to check the state of the Cart stored in session, if there is one. This is necessary because the user may have added items to the Cart and then refreshed the page or navigated away and then back again. In these circumstances, the reloaded page needs to sync itself with the Cart data in session. I can accomplish this with a call performed in the page’s onload function, like so: Cart.getCart(displayCart). Note that displayCart() is a callback function invoked with the Cart response data from the server.

If a Cart is in session already, then the creator will retrieve it and call its getCart() method. If no Cart is in session, then thecreator will instantiate a new one, place it in session, and call the getCart() method.

Listing 7 shows the implementation of the addToCartButtonHandler() function that is called when an item’s Add to Cartbutton is clicked:
Listing 7. addToCartButtonHandler() implementation

/*
 * Handles a click on an Item's "Add to Cart" button
 */
function addToCartButtonHandler() {

  // 'this' is the button that was clicked.
  // Obtain the item ID that was set on it, and
  // add to the cart.
  Cart.addItemToCart(this.itemId,displayCart);
}

 

With DWR taking care of all the communication, the add-to-cart behavior on the client is literally a one-line function. Listing 8 shows the final piece of this jigsaw puzzle — the implementation of the displayCart() callback, which updates the UI with the state of the Cart:
Listing 8. displayCart() implementation

/*
 * Displays the contents of the user's shopping cart
 */
function displayCart(cart) {

  // Clear existing content of cart UI
  var contentsUL = $("contents");
  contentsUL.innerHTML="";

  // Loop over cart items
  for (var item in cart.simpleContents) {

    // Add a list element with the name and quantity of item
    var li = document.createElement("li");
    li.appendChild(document.createTextNode(
                    cart.simpleContents[item] + " x " + item
                  ));
    contentsUL.appendChild(li);
  }

  // Update cart total
  var totalSpan = $("totalprice");
  totalSpan.innerHTML = cart.formattedTotalPrice;
}

 

It’s important to remember here that simpleContents is a JavaScript array mapping Strings to numbers. Each string is the name of an item, and the corresponding number in the associative array is the quantity of that item in the cart. So the expressioncart.simpleContents[item] + " x " + item evaluates to, for example, “2 x Oolong 128MB CF Card“.

The DWR Store application

Figure 3 shows my DWR-based Ajax application in action, displaying items retrieved by a search with the user’s shopping cart on the right:
Figure 3. The DWR-based Ajax store application in action
Screenshot of example scenario, with search results and shopping cart

Back to top

Pros and cons of DWR

Call batching

In DWR, several remote calls can be sent to the server with a single HTTP request. CallingDWREngine.beginBatch() tells DWR to not dispatch subsequent remoted calls straight away, but instead to combine them into a single batched request. A call toDWREngine.endBatch() sends the batched request to the server. The remote calls are executed in order on the server-side, and then each JavaScript callback is invoked.

Batching can help to reduce latency in two ways: first, you avoid the overhead of creating an XMLHttpRequestobject and establishing the associated HTTP connection for each call. Second, in a production environment, the Web server won’t have to deal with so many concurrent HTTP requests, improving response times.

You’ve seen how easy it is to implement a Java-backed Ajax application using DWR. Although the example scenario is a simple one and I’ve taken a fairly minimal approach to implementing the use cases, you shouldn’t underestimate the amount of work the DWR engine can save you over a homegrown Ajax approach. Whereas in previous articles I walked through all the steps of manually setting up an Ajax request and response and converting a Java object graph into a JSON representation, here DWR has done all of that work for me. I wrote fewer than 50 lines of JavaScript to implement the client, and on the server-side, all I had to do was augment my regular JavaBeans with a couple of extra methods.

Of course, every technology has its drawbacks. As with any RPC mechanism, in DWR it can be easy to forget that each call you make on remoted objects is much more expensive than a local function call. DWR does a great job of hiding the Ajax machinery, but it’s important to remember that the network is not transparent — there is latency involved in making DWR calls, and your application should be architected so that remoted methods are coarse-grained. It is for this purpose that addItemToCart() returns the Cart itself. Although it would have been more natural to makeaddItemToCart() a void method, each DWR call to it would then have had to be followed with a call to getCart() to retrieve the modified Cart state.

DWR does have its own solution to the latency issue in call batching (see the sidebar Call batching). If you’re unable to provide a suitably coarse-grained Ajax interface for your application, then use call batching wherever possible to combine multiple remote calls into a single HTTP request.

Separation of concerns

By its very nature, DWR creates a tight coupling between client-side and server-side code, with a number of implications. First, changes to the API of remoted methods need to be reflected in the JavaScript that calls on the DWR stubs. Second (and more significantly), this coupling causes client-side concerns to leak into server-side code. For instance, because not all Java types can be converted into JavaScript, it is sometimes necessary to add extra methods to Java objects so that they can be remoted more easily. In the example scenario, I resolved this by adding a getSimpleContents() method to Cart. I also added thegetCart() method, which is useful in a DWR scenario but otherwise completely redundant. Given the need for a coarse-grained API on remoted objects and the problem of converting certain Java types to JavaScript, you can see how remoted JavaBeans can become polluted with methods that are useful only to an Ajax client.

To get around this, you might use wrapper classes to add extra DWR-specific methods to your plain-old JavaBeans. This would mean that Java clients of the JavaBean classes wouldn’t see the extra fluff associated with remoting, and it would also allow you to give more friendly names to remoted methods — getPrice() instead of getFormattedPrice(), for instance. Figure 4 shows a RemoteCart class that wraps Cart to add the extra DWR functionality:
Figure 4. RemoteCart wraps Cart for remoting functionality
Class diagram of RemoteCart wrapper class

Finally, you need to remember that DWR Ajax calls are asynchronous and should not be expected to return in the order they were dispatched. I ignored this small hurdle in example code, but in the first article of this series, I demonstrated how to timestamp responses as a simple safeguard against data arriving out-of-order.

Back to top

In conclusion

As you’ve seen, DWR has a lot to offer — it allows you to create an Ajax interface to your server-side domain objects quickly and simply, without needing to write any servlet code, object serialization code, or client-side XMLHttpRequest code. It’s extremely simple to deploy to your Web application using DWR, and DWR’s security features can be integrated with a J2EE role-based authentication system. DWR isn’t applicable to every application architecture, however, and it does require you to give some thought to the design of your domain objects’ APIs.

If you want to learn more about the pros and cons of Ajax with DWR, the best thing would be to download it for yourself and start experimenting. While DWR has many features that I haven’t covered, the article source code is a good starting point for taking DWR for a spin. See Resources to learn more about Ajax, DWR, and related technologies.

One of the most important points to take away from this series is that for Ajax applications, there is no one-size-fits-all solution. Ajax is a rapidly developing field with new technologies and techniques emerging all the time. In the three articles of this series, I’ve focused on getting you started with leveraging Java technologies in the Web tier of an Ajax application — whether you choose an XMLHttpRequest-based approach with an object serialization framework or the higher level abstraction of DWR. Look out for further articles exploring Ajax for Java developers in the coming months.

 

Back to top

Download

Description Name Size Download method
DWR source code j-ajax3dwr.zip 301 KB HTTP

Information about download methods

 

Resources

Learn

Get products and technologies

Discuss

About the author

Philip McCarthy is software development consultant specializing in Java and Web technologies. He is currently working on Hewlett Packard’s Digital Media Platform project at HP Labs, Bristol. In recent years Phil has developed several rich Web clients employing asynchronous server communication and DOM scripting. He is glad we now have a name for them. You can get in touch with Phil at philmccarthy@gmail.com.

original:http://www.ibm.com/developerworks/java/library/j-ajax3/index.html