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.
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.
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.
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.
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
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
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
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
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.
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.
| Description | Name | Size | Download method |
|---|---|---|---|
| Part 3 sample code | wa-aj-ajaxpro3.zip | 1087KB | HTTP |
Information about download methods
Learn
-
See how to use DWR within a portlet framework in "Develop an Ajax-based file
upload portlet using DWR" (developerWorks, August 2007).
-
Learn how DWR implements so-called reverse Ajax, a.k.a. Comet, in "Ajax for Java developers: Write scalable Comet applications using Jetty and Direct Web Remoting"(developerWorks, July 2007).
-
Find out how to modernize legacy Web applications using DWR in "Kick-start your Java apps" (developerWorks, December 2007).
-
Explore the world of JPA in "Design enterprise applications with the EJB 3.0 Java Persistence API" (developerWorks, March 2006).
-
Scriptaculous is a key component in the Ajax support in Ruby on Rails. Read about it in
"Crossing borders: Ajax on Rails" (developerWorks, December 2006).
-
Scriptaculous can be combined with many other JavaScript libraries. See how its effects
can be used with the Google Web Toolkit in "Ajax for Java Developers: Exploring the Google Web Toolkit" (developerWorks, June 2006).
-
Check out the in-place editor control in Scriptaculous by reading "Ajax and
XML: Five cool Ajax widgets" (developerWorks, January 2007).
-
Get a survey of popular Ajax libraries, including Scriptaculous in "Ajax -- a guide for the perplexed" (developerWorks, July 2007).
-
Find out more about some of the Ajax patterns mentioned here in "Ajax and XML: Five common Ajax patterns" (developerWorks, March 2007).
-
The server scripts used in this article conform to the REST protocol. Learn more about
using REST and Ajax together in "RESTful Web services and their Ajax-based clients" (developerWorks, July 2007).
-
Prototype is a key component in the Ajax support in Ruby on Rails. Read about it in "Crossing borders: Ajax on Rails" (developerWorks, December 2006).
-
Get a survey of popular Ajax libraries, including Prototype in "Ajax -- a guide for the perplexed" (developerWorks, July 2007).
-
"Discover the Ajax
Toolkit Framework for Eclipse" (November 2006): Extend the Eclipse Web Tools Platform when you add support for open-source Ajax tool kits such as Dojo, Zimbra, and Rico.
-
Read Simon Willison's "A (Re)-Introduction to JavaScript."
-
Check out IBM developerWorks' Ajax Resource Center
-
W3Schools provides online reference information for all core Ajax technologies (JavaScript, CSS, HTML, DOM, XML, and so on).
-
developerWorks technical events and webcasts: Stay current with developerWorks technical events and webcasts.
-
Podcasts: Tune in and catch up with IBM technical experts.
Get products and technologies
-
DWR version 2.0 allows Javascript in a browser to interact with Java on a server and helps you manipulate Web pages with the results.
-
Get the Firebug extension for Firefox.
-
Get the Apache Tomcat 6.0.14.
-
Get MySQL 5.0.41.
-
Get OpenJPA 1.0.2.
Comments (Undergoing maintenance)






