An Introduction To Ajax

Pages: 1, 2

Introducing DWR

As demonstrated in the Raw Ajax section, using the XmlHttpRequest directly for creating asynchronous requests is cumbersome. Not only is it verbose in the JavaScript code, you must consider the server-side work needed to route your Ajax requests to the appropriate service, and marshal the result back to the browser.

DWR was invented to handle all the plumbing required to wire up your Web page to your backend services. It is a Java framework you can easily plug into your Web applications to allow your JavaScript code to call services on the server. It even integrates directly with the Spring Framework to allow you to expose your beans directly to the Web client.

What's really neat about DWR is that once you configure the services you want to expose to the client, it uses reflection to generate JavaScript objects that your Web page can use to access your service. Your Web page then simply interfaces with the generated JavaScript objects as if they were using the service directly; DWR seamlessly takes care of all of the dirty details of Ajax and request routing.

Let's dissect our example code to clarify how this works.

Application in Detail: DWR Dissected

The first thing to notice about the application is that it is a standard Java application using the Layered Architecture design pattern. The fact you will use DWR to expose some of your services via JavaScript doesn't affect your design.

Figure 2
Figure 2.

Here is a simple Java service that we will expose directly to our JavaScript code using the DWR framework:

package com.tearesolutions.service;

public interface AjaxSampleSvc { 
  Article castVote(int rank);
}

This is an incredibly simplified example, as you only have one Article that can be voted on. This service is managed by Spring under the bean name ajaxSampleSvc, and depends on the ArticleDao for its persistence needs. See applicationContext.xml for details.

To configure DWR to expose this service as a JavaScript object, you add the dwr.xml file to the WEB-INF directory:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dwr PUBLIC
 "-//GetAhead Limited//DTD Direct Web Remoting 0.4//EN"
 "http://www.getahead.ltd.uk/dwr/dwr.dtd">
        
<dwr>
 <allow>
  <create creator="spring" javascript="ajaxSampleSvc">
   <param name="beanName" value="ajaxSampleSvc" />
  </create>
  <convert converter="bean" match="com.tearesolutions.model.Article"/>
  <exclude method="toString"/>
  <exclude method="setArticleDao"/>
 </allow>
</dwr>

 

The dwr.xml file tells DWR what services you want to expose directly to your JavaScript code. Notice that you have asked to expose the ajaxSampleSvc Spring bean. DWR will automatically find the Spring ApplicationContext set up by your application. To do this, it requires you to use the standard ContextLoaderListener servlet filter to initialize your Spring ApplicationContext.

DWR is set up as a servlet, and so you add its definition to your web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD 
 Web Application 2.3//EN" "bit.ly/bnsLU6">

<web-app>
 <display-name>Ajax Examples</display-name>

 <listener>
  <listener-class>
      org.springframework.web.context.ContextLoaderListener
  </listener-class>
 </listener>
        
 <servlet>
  <servlet-name>ajax_sample</servlet-name>
  <servlet-class>com.tearesolutions.web.AjaxSampleServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
 </servlet>

 <servlet>
  <servlet-name>dwr-invoker</servlet-name>
  <display-name>DWR Servlet</display-name>
  <description>Direct Web Remoter Servlet</description>
  <servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class>
  <init-param>
   <param-name>debug</param-name>
   <param-value>true</param-value>
  </init-param>
 </servlet>

 <servlet-mapping>
  <servlet-name>ajax_sample</servlet-name>
  <url-pattern>/ajax_sample</url-pattern>
 </servlet-mapping>

 <servlet-mapping>
  <servlet-name>dwr-invoker</servlet-name>
  <url-pattern>/dwr/*</url-pattern>
 </servlet-mapping>
</web-app>

After this is complete, you can load http://localhost:7001/ajax-demo/dwr to see firsthand what services are available. Doing so shows the following:

Figure 3
Figure 3. The available services

Clicking the ajaxSampleSvc link takes you to a sample implementation of how you could use the service directly within your HTML page. Two included JavaScript files do the majority of the work:

<script type='text/javascript' 
   src='/ajax-demo/dwr/interface/ajaxSampleSvc.js'></script>
<script type='text/javascript' 
   src='/ajax-demo/dwr/engine.js'></script>

The ajaxSampleSvc.js is generated dynamically:

function ajaxSampleSvc() { }

ajaxSampleSvc.castVote = function(callback, p0)
{ 
  DWREngine._execute(callback, '/ajax-demo/dwr', 
 'ajaxSampleSvc', 'castVote', p0);
}

Now you can go back and refactor your raw-ajax.html file by replacing all the XmlHttpRequest code with this ajaxSampleSvc JavaScript object. The result of this change can be seen in the dwr-ajax.html file; following are the new JavaScript functions:

function castVote(rank) {
  ajaxSampleSvc.castVote(processResponse, rank);
}
function processResponse(data) {
 var voteText = "<h3>Thanks for Voting!</h3>"
    + "<p>Current ranking: " + data.voteAverage 
    + " out of 5</p>" 
    + "<p>Number of votes placed: " 
    + data.numberOfVotes + "</p>";
 $('votes').innerHTML = voteText;       
}

Amazingly simple, isn't it? The Article domain object returned by the ajaxSampleSvc object is serialized into a JavaScript object, allowing you to call methods on it such as numberOfVotes() and voteAverage(). You use this data within the HTML code that you dynamically generate and insert into the "votes" DIV element.

Future Work

In a follow-up article, I will extend the conversation on Ajax to include these concepts:

  1. Ajax Best Practices

    Like many technologies, Ajax can be a double-edged sword. There are several use cases where Ajax is overkill for an application and actually hampers usability. I will review some anti-patterns to avoid, highlight some of the negative aspects of Ajax, and show mechanisms to help alleviate them. For example, was Ajax a suitable solution for the Netflix movie browser? Or, how do you cue the user that something really is happening, and clicking the button multiple times will not help?

  2. Managing State Across Requests

    When using Ajax, the document DOM is changed from the original and there's often a lot of page state information that gets stored in client-side variables. When users follow a link to another page in your application, that state is lost. When users inevitably click the Back button, they are presented with the original page from the cache. This can make for very confused users!

  3. Debugging Techniques

    Performing more work on the client side using JavaScript requires some debugging tools to help understand what's going on when things aren't going the way you want.

Conclusion

This article introduced Ajax, and showed how you can use it to create a dynamic and responsive Web application. By using the DWR framework, you can easily incorporate Ajax into your site without worrying about all the mundane plumbing work that must be performed.

Special thanks to Joe Walker and his team at the Getahead IT consultancy for making such a wonderful tool in DWR