Skip to main content

Develop AJAX applications like the pros, Part 3: Use DWR, Java, and the Dojo Toolkit to integrate Java and JavaScript

Michael Galpin (mike.sr@gmail.com), Software architect, eBay
Michael Galpin's photo
Michael Galpin has been developing Java software professionally since 1998. He currently works for eBay. He holds a degree in mathematics from the California Institute of Technology.

Summary:  Quick, how many Java™ Web development frameworks, libraries, and toolkits can you name? The are so many out there that it can be overwhelming just trying to figure out what does what and which one can actually help you solve your problems. However, if you are doing Ajax development, there is one library that you absolutely need to know: Direct Web Remoting (DWR). This library leverages the Java language and Java Web technologies to greatly simplify Ajax development. It has set the standard for how to integrate Ajax seamlessly into a Java web application. In fact, DWR joined the Dojo foundation, a broad coalition of popular, open source Ajax technologies. In this article, see just how easy Ajax can be using DWR.

View more content in this series

Date:  05 Aug 2008
Level:  Intermediate PDF:  A4 and Letter (303KB)Get Adobe® Reader®
Activity:  4648 views

This is the third, and final, article of a three-part series on popular JavaScript libraries that you can use to create Ajax-powered applications. In Part 1, you learned about the Prototype library to build a Web application for managing songs. Part 2 described using the Scriptaculous library to build a Web application for managing photos. This article shows you how to leverage DWR to simplify Ajax development.

This article uses version 2.0 of DWR. The sample code requires Java 5+ because it uses both generics and annotations. The sample application uses a combination of MySQL 5.12 and Tomcat 6.0.14. However, you should be able to switch those out without too much trouble. The application also uses the Java Persistence API (JPA) for data access as well as OpenJPA 1.0 for its JPA implementation. Again, this can be switched out for other JPA implementations such as Hibernate, Kodo, and so on. This article uses the Firebug plug-in for Firefox as it is a great tool for Ajax debugging. Get links to these tools in Resources.

Introduction to Direct Web Remoting (DWR)

Ajax applications can seem magical at first, but the process of developing the applications is straightforward, thankfully. For every Ajax interaction, you must create an endpoint (to borrow a term from our Web service friends) on a server and client-side code to call that endpoint. You must also create all the code for serializing the data going between the client and server. These server endpoints can be generic services, maybe even a RESTful one at that. However, they are more often very specific to the needs of the client. Sometimes you should avoid tight coupling, but sometimes you should embrace it. For the latter cases, DWR is a turnkey solution. It lets you declaratively expose server-side code as an Ajax endpoint, generating all the plumbing for you auto-magically. Now, take a look at a specific example to see how DWR works.


Sample application: Ajax message board

The sample application used here is a simple message board. The data model is greatly simplified so you can concentrate on the DWR-brokered Ajax interactions. You will start by examining the application's back end to see how DWR can be layered on top of it to enable the Ajax functions of the application.


Setting up the back end

You start by creating a database table for the message board. Listing 1 shows the SQL script for creating this table.


Listing 1. Messages table SQL script

CREATE TABLE 'messages' (
  'id' int(11) NOT NULL auto_increment,
  'title' varchar(40) NOT NULL,
  'body' text,
  'created_at' timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
  'author' varchar(40) NOT NULL default 'anonymous',
  PRIMARY KEY  ('id')
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

The only thing to really note here is that you need to supply a title, body, and author for each message on the message board. The SQL script takes care of the rest. The script shown is for MySQL, but can be easily adapted to other RDBMS's. For data access, I will use JPA. The Java class corresponding to the table is shown in Listing 2.


Listing 2. The message Java class

package org.developerworks.msgb;

import java.util.Date;

import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Id;
import javax.persistence.GeneratedValue;
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Column;

import org.directwebremoting.annotations.DataTransferObject;

@DataTransferObject
@Entity
@Table(schema="msgb", name = "messages")
public class Message {
     @Id
     @GeneratedValue(strategy=IDENTITY)
     private int id;
     
     @Column(name="title")
     private String title;
     
     @Column(name="author")
     private String author;
     
     @Column(name="body")
     private String body;
     
     @Column(name="created_at")
     private Date createdAt;

     public int getId() {
          return id;
     }

     public void setId(int id) {
          this.id = id;
     }

     public String getTitle() {
          return title;
     }

     public void setTitle(String title) {
          this.title = title;
     }

     public String getAuthor() {
          return author;
     }

     public void setAuthor(String author) {
          this.author = author;
     }

     public String getBody() {
          return body;
     }

     public void setBody(String body) {
          this.body = body;
     }

     public Date getCreatedAt() {
          return createdAt;
     }

     public void setCreatedAt(Date createdAt) {
          this.createdAt = createdAt;
     }
}

Most of this is just boilerplate code: several fields with getters and setters. And, most of the annotations are just standard JPA annotations that map the Java fields to database columns. There is one unusual annotation you should notice, the @DataTransferObject annotation. This is a DWR annotation that lets DWR know this class can be automatically marshaled and sent across the wire as part of an Ajax response. DWR includes several converters for marshalling Java types. You could also optionally specify which fields should be included/excluded by DWR, again through the use of annotations. Now that you've got the data access set up, take a look at creating a service for the application.


Creating remote services

The sample application does two simple things. It lists all of the messages posted to the message board, and it allows a user to post a new message to the message board. With that in mind, take a look at Listing 3, which shows the service code for the application.


Listing 3. The MessageService class

package org.developerworks.msgb;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

import org.directwebremoting.annotations.RemoteMethod;
import org.directwebremoting.annotations.RemoteProxy;

@RemoteProxy
public class MessageService {
     private EntityManagerFactory factory;
     private EntityManager em;
     
     public MessageService(){
          factory = Persistence.createEntityManagerFactory("msgb");
          em = factory.createEntityManager();
     }
     
     @RemoteMethod
     @SuppressWarnings("unchecked") // thanks type erasure
     public List<Message> getMessages(){
          List<Message> messages = em.createQuery("select m from 
		                                  Message m").getResultList();
          return messages;
     }
     
     @RemoteMethod
     public void saveMessage(Message msg){
          em.getTransaction().begin();
          em.persist(msg);
          em.getTransaction().commit();
     }
}

The code here is just standard JPA code for querying and creating messages. Again, the interesting code is in the annotations. The class is annotated as a RemoteProxy. This tells DWR that remote clients can call methods in this class. Of course, the mechanism for those remote calls is Ajax using DWR. Each method in the class is also explicitly exposed to remote clients using the RemoteMethod annotation. If you have a method that you do not want to expose, simply omit the annotation. All of the code written so far is just back-end code plus some annotations. There is one more thing you need to do to setup DWR on your back-end.


The DWR servlet

DWR uses a Java servlet to accept and respond to Ajax requests. This is included with the DWR library, so all you need to do is configure your Web application to enable the servlet in your application's web.xml file. Listing 4 shows the web.xml for the sample application.


Listing 4. Message board web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns="http://java.sun.com/xml/ns/javaee"
     xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
	     http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
     id="msgb" version="2.5">
     <display-name>MessageBoard</display-name>
     <welcome-file-list>
          <welcome-file>index.html</welcome-file>
     </welcome-file-list>
     <servlet>
          <display-name>DWR Servlet</display-name>
          <servlet-name>dwr-invoker</servlet-name>
          <servlet-class>
               org.directwebremoting.servlet.DwrServlet
          </servlet-class>
          <init-param>
               <param-name>debug</param-name>
               <param-value>true</param-value>
          </init-param>
          <init-param>
               <param-name>classes</param-name>
               <param-value>org.developerworks.msgb.MessageService, 
			   org.developerworks.msgb.Message</param-value>
          </init-param>
     </servlet>
     <servlet-mapping>
          <servlet-name>dwr-invoker</servlet-name>
          <url-pattern>/dwr/*</url-pattern>
     </servlet-mapping>
</web-app>

This simply exposes the DWR servlet and sends any URL starting with /dwr/ to the servlet. Also, notice the init-params. The classes param is a comma-separated list of all classes that are annotated with DWR annotations. Note that, if you do not like annotations, you can use an XML file to capture the same information. This is all you have to do to your back-end, so now you can start using DWR on the front end of your application.


Building the front end

DWR makes it easy to enable Ajax on the back-end of an application. All you must do is add some declarations and activate the DWR servlet. But what about the front end? Most JavaScript toolkits have some libraries that you use. You reference the libraries in your Web pages and read the documentation on them to find out how to use them. However, this is not the case for DWR.

DWR generates JavaScript for you dynamically. Luckily, DWR also makes it easy to find out how to use that JavaScript. Take a look again at your web.xml file in Listing 4. Notice the init-param called debug? That setting lets you introspect the dynamic DWR JavaScript created by DWR. Simply deploy your Web application and go to the following URL: http://<root>/<web_app_name>/dwr, as shown in Figure 1.


Figure 1. The DWR debug screen
The DWR debug screen

This shows all of your Java classes that you have annotated with the @RemoteProxy declaration. Clicking on any of the classes displays its details, as shown in Figure 2.


Figure 2. MessageService JavaScript information
MessageService JavaScript information

This interface shows you how to reference the dynamic JavaScript on any of your Web pages. There is a core library (engine.js) that you must include and a purely dynamic library (MessageService.js) that is specific to the application. There is an optional utility library that provides numerous helper functions.

Now that you know how to reference the DWR JavaScript, you still need to find out how to use it. The MessageBoard page allows you to test it, as shown in Figure 3.


Figure 3. Testing DWR Ajax
Testing DWR Ajax

This shows you the result of an Ajax request to the getMessages() call. The data is shown as an array of JavaScript objects (JSON). You know what the API will return, but how do you call it? Just take a look at the source of the page, as shown in Listing 5.


Listing 5. Source of the MessageBoard test page

<li>
  getMessages(  );
  <input class='ibutton' type='button' onclick='MessageService.getMessages(reply0);'
     value='Execute'  title='Calls MessageService.getMessages(). 
     View source for details.'/>
  <script type='text/javascript'>
    var reply0 = function(data)
    {
      if (data != null && typeof data == 'object') 
	            alert(dwr.util.toDescriptiveString(data, 2));
      else dwr.util.setValue('d0', dwr.util.toDescriptiveString(data, 1));
    }
  </script>
  <span id='d0' class='reply'></span>
</li>
<li>
  saveMessage(    <input class='itext' type='text' size='10' value='' 
    id='p10' title='Will be converted to: org.developerworks.msgb.Message'/>  );
        <input class='ibutton' type='button' onclick='MessageService.saveMessage
       (objectEval($("p10").value), reply1);' value='Execute'  title='Calls 
	    MessageService.saveMessage(). View source for details.'/>
  <script type='text/javascript'>
    var reply1 = function(data)
    {
      if (data != null && typeof data == 'object') alert(dwr.util.
	                                                 toDescriptiveString(data, 2));
      else dwr.util.setValue('d1', dwr.util.toDescriptiveString(data, 1));
    }
  </script>

  <span id='d1' class='reply'></span>
</li>

So for example, to call getMessages, you use MessageService.getMessages(callbackFunction). You saw what will get passed to your callbackFunction when you tested the method. Now you are ready to build your Web page. It is shown in Listing 6.


Listing 6. MessageBoard Web page

 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN"
 "http://www.w3.org/TR/html4/strict.dtd">
 <html>
<head>
     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
     <title>The developerWorks Message Board</title>
      <script type="text/javascript" src="/MessageBoard/dwr/interface/
                                      MessageService.js"></script>
      <script type="text/javascript" src="/MessageBoard/dwr/
                                      engine.js"></script>
      <script type="text/javascript" src="/MessageBoard/dwr/
                                      util.js"></script>
      <script type="text/javascript">
           var columns = ["title", "author", "body", "createdAt"];
           function loadData(){
                MessageService.getMessages(addRows);
         }
           // messages should be an array of Message hashes
           function addRows(messages){
                var cellfuncs = [];
                for each (var prop in columns){
                     cellfuncs.push(createFunction(prop));
                }
                dwr.util.addRows("messageTableBody", messages, cellfuncs);     
           }
           function createFunction(prop){
                return function(msg) { return msg[prop]; };
           }
           function sendMsg(){
                var msg = {};
                msg.title = dwr.util.getValue("title");
                msg.author = dwr.util.getValue("author");
                msg.body = dwr.util.getValue("body");
                MessageService.saveMessage(msg);
                msg.createdAt = Date();
                var messages = [msg];
                addRows(messages);
                clearForm();
           }
           function clearForm(){
                dwr.util.setValue("title","");
                dwr.util.setValue("author","");
                dwr.util.setValue("body","");
           }
      </script>     
      <style type="text/css">
           label {
               float: left;
               text-align: right;
               margin-right: 15px;
               width: 100px;
          }
          #messageTableHead {
               font-weight: 900;
               color:navy;
          }
          body {
               color:MidnightBlue;
               background-color:PaleTurquoise;
               margin:20px;
               padding:0px;
               font:11px verdana, arial, helvetica, sans-serif;
          }
          .pageTitle {
               margin:0px 0px 15px 0px;
               padding:0px;
               font-size:28px;
               font-weight:900;
               color:#aaa;
          }
          #formDiv{
               padding-top:12px;
               color:Indigo;
          }
      </style>
</head>
<body onLoad="loadData()">
     <div class="pageTitle">The developerWorks Message Board</div>
     <div class="pageContent">
          <table id="messageTable" border="1" cellpadding="4">
               <thead id="messageTableHead">
                    <tr>
                         <td>Title</td>
                         <td>Author</td>
                         <td>Message</td>
                         <td>Posted</td>
                    </tr>
               </thead>
               <tbody id="messageTableBody">
               </tbody>
          </table>
     </div>
     <div id="formDiv">
          <form id="messageForm">
               <label>Message Title:</label><input type="text" 
	                                name="title" id="title"/><br/>
               <label>Your Name:</label><input type="text"
	                              name="author" id="author"/><br/>
               <textarea name="body" cols="80" rows="15"
	                              id="body"></textarea><br/>
               <input type="button" id="msgBtn" value="Add Message"
	                              onClick="sendMsg()"/>
          </form>
     </div>
</body>
</html>

Let's analyze this code. First off, it is simply an HTML file. It is not a JSP or a JSF page, just static HTML with some JavaScript and CSS. When the page loads it calls the loadData() function. This function uses the MessageService.getMessages() call that you just learned about. It passes in the addRows function to handle the response to the Ajax request that DWR makes for us.

The addRows() function takes the data returned from the server and uses one of the utility functions provided by DWR. This is dwr.util.addRows., which takes the ID of either an HTML table, table header, table footer, or table body and appends rows to them. It uses an array of functions (cellfuncs in the code) where each function is used to extract and transform the data so it can be put into a table cell. Thus, the data in the (i,j)th element of the table will be cellfuncs[j](messages[i]). This is a very succinct way to map the data into the table. Bringing up the page in a browser should look like Figure 4.


Figure 4. MessageBoard page
MessageBoard page

The page loads the data asynchronously using your DWR-enabled Ajax. So, you can display messages, but what about saving new ones? The form at the bottom of your page is how you do that. When you click Add Message, you use DWR's utility method dwr.util.getValue to get the data from your form. This utility function works on any HTML element, from a div to text input or a checkbox or select-list. You take the data, put it into a JavaScript object and call MessageService.saveMessage(). In this case, you did not specify a handler because the response is a void. Instead, you immediately reuse the addRows function that you used earlier to append the new row to your table. This makes your UI very responsive.

You can test out the page by simply filling out the form and pressing Add Message. It is useful to use a tool like Firebug to watch the HTTP traffic. Listing 7 shows the data being sent on a saveMessage call.


Listing 7. The saveMessage call

callCount=1
page=/MessageBoard/
httpSessionId=
scriptSessionId=B88B0681A9BB674C14786B7DCA3EA6E3153
c0-scriptName=MessageService
c0-methodName=saveMessage
c0-id=0
c0-e1=string:The%20One%20I%20Love
c0-e2=string:Michael
c0-e3=string:This%20goes%20out%20to%20the%20one%20I%20love.
                  %20This%20one%20goes%20out%20to%20the%20one
%20I%20left%20behind.
c0-param0=Object_Object:{title:reference:c0-e1, 
                  author:reference:c0-e2, body:reference:c0-e3}
batchId=1

Figure 7 shows the format of the data that DWR sends across. It is not really important—part of the point of frameworks like DWR is that you do not have to worry about things like this. But it is interesting to see how it works.


Summary

There are many Ajax toolkits out there that make various parts of Ajax development easier. DWR is an end-to-end toolkit for Java Web applications, and makes it easy to create both the server and client sides of the application. The server-side only requires a few Java annotations to turn any service into an Ajax service. On the client side, you get an API that mirrors what you have on the server. All you add is a callback function. It really cannot get much simpler.



Download

DescriptionNameSizeDownload method
Part 3 sample codewa-aj-ajaxpro3.zip1087KB HTTP

Information about download methods


Resources

Learn

Get products and technologies

About the author

Michael Galpin's photo

Michael Galpin has been developing Java software professionally since 1998. He currently works for eBay. He holds a degree in mathematics from the California Institute of Technology.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Web development
ArticleID=327300
ArticleTitle=Develop AJAX applications like the pros, Part 3: Use DWR, Java, and the Dojo Toolkit to integrate Java and JavaScript
publish-date=08052008
author1-email=mike.sr@gmail.com
author1-email-cc=

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Special offers