
This hands-on lab takes you through the basics of using DWR (Direct
Web
Remoting) for
developing
AJAX-based web applications. This document is designed to get you going
as quickly as possible.
In this lab, you will learn (1) step by step of building DWR based AJAX application (2) How to configure your web application configuration file web.xml to be DWR aware (3) How to use write DWR configuration file called dwr.xml (4) How to use some of the DWR utility functions.
Expected duration: 90-120 minutes
This tutorial assumes you have some basic knowledge of, or programming experience with, the following technologies.
Before you begin, you need to install the following software on your computer. The dwr.jar file is already included as part of the hands-on lab zip file so you don't have to download it yourself.
In this exercise, you are going to build
and run "Chat demo" sample application (dwr.examples.chat
NetBeans project) using NetBeans. All the
required code and library files are already provided as a ready-to-run
NetBeans project.
1. Open dwr.examples.chat NetBeans project.

Figure-1.10: Open dwr.examples.chat NetBeans project
2. Resolve a
reference problem.


Figure-1.12: Resolve


3. Build and run the project

Figure-1.16: Run

Figure-1.12: Do some chatting
Figure-1.13: Running Data Validation using AJAX
DWR provides a testing page for the
remoted methods. All you have to do is to append /dwr to the URL.
1. From your browser, go to http://localhost:8080/dwr.examples.chat/dwr.
This page gives you a debugging page in which you can interact with the
backend application.
2. Click Chat. (Figure-1.13
below) It will display all the methods that are remoted by DWR.

Figure-1.13: Debugging page
3. Now you see there are two
methods - addMessage("") and getMessage() - are remoted by DWR,
which means these two methods can be invoked through the matching
JavaScript object.
4. Click Execute button of
the getMessages() method.
(Figure-1.14 below)

Figure-1.14: Debugging page, Click Execute button of the
getMessages() method
5. You should see Figure-1.15
below.
This shows the data that has been returned in the form of JSON as a
result of invoking
getMessages() method of the JavaScript object Chat. (Note: We will learn about
JSON later in this course.)

Figure-1.15: Returned data
6. Type in whatever string you
like - 3rd message in the
example below - into the gray area of the addMessage() call and click Execute button. (Figure-1.16
below).

Figure-1.16: Debugging page, Click Execute button of the addMessage()
method
7. You should see Figure-1.17 below. This shows the data that has been returned as a result of invoking addMessage() method of the JavaScript object Chat.

Figure-1.17: Returned data
In this step, you will learn the
underlying architecture of DWR by looking into the pieces that make up
the dwr.examples.chat sample
application.
1. First of all, you have to have dwr.jar file in your web
application. The dwr.examples.chat
application has it. You can see that by expanding dwr.examples.chat project node then Libraries. You should see dwr.jar file. (Figure-1.30 below)
Note: The dwr.jar file can be downloaded from DWR home site.

Figure-1.30: dwr.jar
2. You have to include the DWR servlet
mapping in the web.xml of
your web application. Every DWR application has to have
this. The debug parameter could be set to false or absent.
|
<servlet> <servlet-name>dwr-invoker</servlet-name> <display-name>DWR Servlet</display-name> <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>dwr-invoker</servlet-name> <url-pattern>/dwr/*</url-pattern> </servlet-mapping> |
You can see the web.xml file of the dwr.examples.chat
application under Web
Pages->WEB-INF node. (Figure-1.33 below)

Figure-1.33: The web.xml file has to have DWR servlet mapping
3. You then have to have DWR
configuration file called dwr.xml
in the same directory you have web.xml
file. In this file, you specify which Java classes and which
methods of those Java classes you want to do "remoting". You can
see the the dwr.xml file of the dwr.examples.chat
application under Web
Pages->WEB-INF node. (Figure-1.34 below)

Figure-1.34: The dwr.xml file
In this example, the dwr.xml indicates that all the
public methods of the mypackage.Chat
Java class will be remoted as Chat JavaScript
object. Now let's take a look at the mypackage.Chat Java class and how
the methods of it gets called from the browser.
4. Double click Chat.java under Source Packages->mypackage to
open it in the source code editor window. (Figure-1.35 below)

Figure-1.35: mypackage.Chat.java

1. Double-click Chat.java
under Source Packages->mypackage
to open in the source editor.
2. Modify the Chat.java
file
as
following. The new code fragments that need to be added are
highlighted in bold
and blue colored font.
| /* * Chat.java * * Created on May 29, 2006, 8:03 AM * * To change this template, choose Tools | Template Manager * and open the template in the editor. */ package mypackage; import java.util.LinkedList; import java.util.List; public class Chat{ private static int numberOfMessages = 0; public List addMessage(String text){ if (text != null && text.trim().length() > 0){ messages.addFirst(new Message(text)); while (messages.size() > 10){ messages.removeLast(); } numberOfMessages++; } return messages; } public List getMessages(){ return messages; } public int getNumberOfMessages(){ return numberOfMessages; } private static LinkedList messages = new LinkedList(); } |
3. Right click dwr.examples.chat
project node and select Run.
What we want to do test at this point is to make sure the new method
we've added is remoted.
4. From your browser, go to http://localhost:8080/dwr.examples.chat/dwr.
This page gives you a debugging page in which you can interact with the
backend application.
5. Click Chat. (Figure-1.x
below) It will display all the methods that are remoted by DWR.
It should include getNumberOfMessages(). You can also click Execute
button of the method. And it should return the number.
(Figure-1.40 below)

Figure-1.40: Verify that the new method you've added is correctly
remoted.
Now you can be assured that the new
method has been correctly remoted by the DWR. Now let's change the
client side.
6. Double-click index.html under Web Pages to open in the source
editor.
7. Modify the index.html file
as
following. The new code fragments that need to be added are
highlighted in bold and
blue-colored
font.
| <!DOCTYPE HTML PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Chat-Demo</title> <!-- You have to include these two JavaScript files --> <script type='text/javascript' src='dwr/engine.js'></script> <script type='text/javascript' src='dwr/util.js'></script> <!-- This JavaScript file is generated specifically for your application --> <script type='text/javascript' src='dwr/interface/Chat.js'></script> <script type='text/javascript'> function sendMessage(){ var text = DWRUtil.getValue("text"); DWRUtil.setValue("text", ""); // Invoke addMessage(text) method of the Chat class on // the server. The gotMessages is a callback function. Chat.addMessage(text, gotMessages); } function checkMessages(){ Chat.getMessages(gotMessages); // This code will not work - you do not want to call the method // synchronously. var chatcount = Chat.getNumberOfMessages(); DWRUtil.setValue("chatcount", chatcount); } // Callback function function gotMessages(messages){ var chatlog = ""; for (var data in messages){ chatlog = "<div>" + messages[data].text + "</div>" + chatlog; } DWRUtil.setValue("chatlog", chatlog); setTimeout("checkMessages()", 1000); } </script> </head> <body onload="setTimeout('checkMessages()', 1000)"> <p>Messages:</p> <div id="chatlog" style="border: 1px solid black;"></div> <p>Number of messages:</p> <div id="chatcount" style="border: 1px solid black;" ></div> <p> Your Message: <input id="text" /> <input type="button" value="Send" onclick="sendMessage()" /> </p> </body> </html> |
8. Right click dwr.examples.chat
project node and select Run.
You will see the following result. (Figure-1.43 below)

Figure-1.43: The Number of messages field is not getting updated.
You noticed that the above is not working
as you expected. Somehow the chatcount
is not being
displayed. There is a reason for that. Think about for a
moment why this code would not work.
9. Modify the index.html file as following. The new code fragments that need to be added are highlighted in bold and blue colored font.
| <!DOCTYPE HTML PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Chat-Demo</title> <!-- You have to include these two JavaScript files --> <script type='text/javascript' src='dwr/engine.js'></script> <script type='text/javascript' src='dwr/util.js'></script> <!-- This JavaScript file is generated specifically for your application --> <script type='text/javascript' src='dwr/interface/Chat.js'></script> <script type='text/javascript'> function sendMessage(){ var text = DWRUtil.getValue("text"); DWRUtil.setValue("text", ""); // Invoke addMessage(text) method of the Chat class on // the server. The gotMessages is a callback function. Chat.addMessage(text, gotMessages); } function checkMessages(){ Chat.getMessages(gotMessages); } function checkChatCount(){ // The getNumberOfMessages() needs to be called in asynchronous fashion Chat.getNumberOfMessages(gotChatCount); } // Callback function function gotMessages(messages){ var chatlog = ""; for (var data in messages){ chatlog = "<div>" + messages[data].text + "</div>" + chatlog; } DWRUtil.setValue("chatlog", chatlog); setTimeout("checkMessages()", 1000); // The checkChatCount() function gets called in 1000 ms. setTimeout("checkChatCount()", 1000); } // Callback function of the Chat.getNumberOfMessages(gotChatCount) function gotChatCount(ChatCount){ DWRUtil.setValue("chatcount", ChatCount); } </script> </head> <body onload="setTimeout('checkMessages()', 1000)"> <p>Messages:</p> <div id="chatlog" style="border: 1px solid black;"></div> <p>Number of messages:</p> <div id="chatcount" style="border: 1px solid black;" ></div> <p> Your Message: <input id="text" /> <input type="button" value="Send" onclick="sendMessage()" /> </p> </body> </html> |
10. Right click dwr.examples.chat
project node and select Clean and
Build Project, Deploy Project,
then Run in the
sequential order. You
will see the following result. (Figure-1.45 below)

Figure-1.45: The Number of messages are correctly reflected now
| public String
getLastMessage(){ if (messages.size()== 0) { return ""; } //getFirst will actually return the "last" message based on this data //structure Message m = (Message)messages.getFirst(); return (String)m.getText(); } |
In this exercise, you have built and run
a simple DWR sample application. You learned how DWR application
is structured. You also add another Java method for remoting.
In this exercise, you will build and run another DWR sample application called "Dynamic Form Editing". Here you are going to exercise on how to pass a JavaBean object called Person as a parameter on a remoted method. All the required code and library files for step (2.1) are already provided as a ready-to-run NetBeans project. For step (2.2), you are going to follow instruction in this document to change code. Step (2.3) is left for your own exercise.
1. Open dwr.examples.form-editing NetBeans
project
3. Build and run the project.

Figure-2.10: Result of running "Dynamic Form Editing" sample DWR
application

Figure-2.11: Write a form
In this step, you are going to modify name field of the Person class to be a JavaBean class
from
its current String type.
The Person class is
considered as a
nested class once it contains another class as a child.
1. Double click Person.java under dwr.examples.form-editing->Source Packages->uk.ltd.getahead.testdwr
to open it in the source editor of NetBeans.
2. Modify the Person.java as
shown below - Change String type
of name field to Name class. The code fragment that
needs to be changed are in bold and
blue-colored font. There are 4 places
you need to change from String to
Name in the Person.java file.
| package uk.ltd.getahead.testdwr; public class Person { private Name name; private String address; private int id; private float salary; // No arg constructor to make it a JavaBean public Person(){ } public Person(Name name, String address, int id, float salary){ this.name = name; this.address = address; this.salary = salary; this.id = id; } public Name getName() { return name; } public void setName(Name name) { this.name = name; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public float getSalary() { return salary; } public void setSalary(float salary) { this.salary = salary; } public int getId() { return id; } public void setId(int id) { this.id = id; } } |
3. Now we need to create Name class. Right-click uk.ltd.getahead.testdwr package node
under dwr.examples.form-editing->Source Packages and select New->Java Class.
(Figure-2.21 below)

Figure-2.21: Create a New Java Class
4. For Class
Name: field under Name and
Location pane, type in Name.
Click Finish.
(Figure-2.22 below)

Figure-2.22: Name class
5. Modify IDE generated Name.java code as shown below.
(Code-2.23
below) The code fragments that need to be added are highlighted
in bold and
blue-colored font. Make sure you have no arg
constructor. Otherwise, DWR will have a problem in conversion of
the class.
| package uk.ltd.getahead.testdwr; /** * * @author sang */ public class Name { private String firstname; private String middlename; private String lastname; // No arg constructor public Name() { } public Name(String firstname, String middlename, String lastname) { this.firstname = firstname; this.middlename = middlename; this.lastname = lastname; } public String getFirstname() { return firstname; } public void setFirstname(String firstname) { this.firstname = firstname; } public String getMiddlename() { return middlename; } public void setMiddlename(String middlename) { this.middlename = middlename; } public String getLastname() { return lastname; } public void setLastname(String lastname) { this.lastname = lastname; } } |
| private void
createPeople() { Person fred = new Person(new Name("Fred", "Middle", "Shin"), "1 Red Street", 2, 100000.0f); Person jim = new Person(new Name("Jim", "M", "Clinton"), "42 Brown Lane", 3, 20000.0f); Person shiela = new Person(new Name("Shiela", "Mi", "Hutcherson"), "12 Yellow Road", 4, 3000000.0f); people.put(new Integer(fred.getId()), fred); people.put(new Integer(jim.getId()), jim); people.put(new Integer(shiela.getId()), shiela); } private static final String SESSION_CLICKS = "sessionClicks"; private static final String CONTEXT_CLICKS = "contextClicks"; private static int nextId = 10; private final Map people = new HashMap(); private Person person = new Person(new Name("John Doe", "Mid", "Thompson"), new Date().toString(), 1, 100000.0F); } |
| <!DOCTYPE HTML PUBLIC
"-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>DWR Examples from DWR website</title> <script type='text/javascript' src='dwr/engine.js'></script> <script type='text/javascript' src='dwr/interface/Demo.js'></script> <script type='text/javascript' src='dwr/util.js'></script> <script type='text/javascript'> // This function gets called when window is loaded. // The fillForm is a callback function to be called asynchronously. function readPerson() { Demo.getExamplePerson(fillForm); } function writePerson() { DWRUtil.getValues(person); DWRUtil.getValues(person.name); DWRUtil.getValues(person.address); Demo.setExamplePerson(null, person); } function clearPerson() { //person = { id:"", name:"", address:"", salary:"" }; person = { id:"", firstname:"", middlename:"", lastname:"", address:"", salary:"" }; DWRUtil.setValues(person); } var person; var name; // Callback function function fillForm(aperson) { // Add some alert messages to display the data that is received from the server alert(DWRUtil.toDescriptiveString(aperson)); alert(DWRUtil.toDescriptiveString(aperson.name)); person = aperson; DWRUtil.setValues(person); // Note the easy syntax for accessing the Name child object from Person parent object name = aperson.name; DWRUtil.setValues(name); } // Initialization that is called when window is loaded function init() { DWRUtil.useLoadingMessage(); readPerson(); } // Call init function when window is loaded window.onload = init; </script> </head> <body> <h1>Dynamic Form Editing</h1> <p>This is a very simple demonstration of form editing using DWR.</a> <h2>Demo</h2> <table> <tr> <td>First Name:</td> <td><input id="firstname" type="text"/></td> <td>Middle Name:</td> <td><input id="middlename" type="text"/></td> <td>Last Name:</td> <td><input id="lastname" type="text"/></td> </tr> <tr> <td>Salary:</td> <td><input id="salary" type="text"/></td> </tr> <tr> <td>ID:</td> <td><input id="id" type="text"/></td> </tr> <tr> <td>Address:</td> <td><input type="text" id="address"/></td> </tr> <tr> <td colspan="2" align="right"> <input type="button" value="Clear" onclick="clearPerson()"/> <input type="button" value="Read" onclick="readPerson()"/> <input type="button" value="Write" onclick="writePerson()"/> </td> </tr> </table> </body> </html> |
| <?xml version="1.0"
encoding="UTF-8"?> <!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="Demo" scope="session"> <param name="class" value="uk.ltd.getahead.testdwr.Demo"/> </create> <create creator="new" javascript="Chat"> <param name="class" value="uk.ltd.getahead.testdwr.Chat"/> </create> <create creator="new" javascript="UrlValidator"> <param name="class" value="org.apache.commons.validator.UrlValidator"/> </create> <create creator="new" javascript="CreditCardValidator"> <param name="class" value="org.apache.commons.validator.CreditCardValidator"/> </create> <create creator="script" javascript="EmailValidator" scope="application"> <param name="language" value="beanshell"/> <param name="script"> import org.apache.commons.validator.EmailValidator; return EmailValidator.getInstance(); </param> </create> <create creator="new" javascript="XOM"> <param name="class" value="uk.ltd.getahead.testdwr.XOMDemo"/> </create> <create creator="new" javascript="JDOM"> <param name="class" value="uk.ltd.getahead.testdwr.JDOMDemo"/> </create> <create creator="new" javascript="DOM4J"> <param name="class" value="uk.ltd.getahead.testdwr.DOM4JDemo"/> </create> <create creator="new" javascript="DOM"> <param name="class" value="uk.ltd.getahead.testdwr.DOMDemo"/> </create> <convert converter="bean" match="uk.ltd.getahead.testdwr.Person"/> <convert converter="bean" match="uk.ltd.getahead.testdwr.Message"/> <convert converter="bean" match="uk.ltd.getahead.testdwr.Name"/> <convert converter="bean" match="$Proxy*"/> </allow> </dwr> |
7. Right click dwr.examples.form-editing project
and select Run.
You should see an alert message displaying the content of the returned
data. (Figure-2.27 below)

Figure-2.27: First alert message within the callback function
8. Click OK
of the alert message. You
should the see the second alert message, which displays the content of
the Name object.
9. Click OK of the second
alert message. (Figure-2.28 below)

Figure-2.28: Second alert message within the callback function.


For your own exercise, make Address class
to be a JavaBean class (instead of String
type). Write Address.java
as shown in Code-2.30 below. Make modifications in Demo.java,
index.html, and dwr.xml
files as you did in step (2.2) above. Rebuild
and test the application.
| package uk.ltd.getahead.testdwr; /** * * @author sang */ public class Address{ private String street; private String state; private String zipcode; /** Creates a new instance of Address*/ public Address() { } public Address(String street, String state, String zipcode) { this.street = street; this.state = state; this.zipcode = zipcode; } public String getstreet() { return street; } public void setstreet(String street) { this.street = street; } public String getstate() { return state; } public void setstate(String state) { this.state = state; } public String getzipcode() { return zipcode; } public void setzipcode(String zipcode) { this.zipcode = zipcode; } } |
In this exercise, you have learned how to
pass Java object as a parameter for a remoted method. You also
learned how to pass a
nested Java object as a parameter.
Return to the top
In this exercise, you will build and run
another DWR sample application called "Dynamic Table
Editing"(dwr.examples.table-editing project).
You will
get to learn how to use DWRUtil.addRows()
and DWRUtil.removeAllRows()
utility functions for manipulating tables.
Before you do this exercise, please get yourself familiarized with these two utility functions by reading Generating Tables tutorial from DWR website. All the required code and library files for step (3.1) are already provided as a ready-to-open-and-run NetBeans project.


Figure-3.11: Edit Shiela's address

Figure-3.12: Add another row

| package uk.ltd.getahead.testdwr; public class Person { /* Getters and setters go here */ private String name; private String address; private int id; private float salary; private String hobby; // No arg constructor to make it a JavaBean public Person(){ } public Person(String name, String address, int id, float salary, String hobby){ this.name = name; this.address = address; this.salary = salary; this.id = id; this.hobby = hobby; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public float getSalary() { return salary; } public void setSalary(float salary) { this.salary = salary; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getHobby() { return hobby; } public void setHobby(String hobby) { this.hobby= hobby; } } |
| /** * Setup the list of people. */ private void createPeople() { Person fred = new Person("Fred", "1 Red Street", 2, 100000.0f, "Swimming"); Person jim = new Person("Jim", "42 Brown Lane", 3, 20000.0f, "Golfing"); Person shiela = new Person("Shiela", "12 Yellow Road", 4, 3000000.0f, "Dancing"); people.put(new Integer(fred.getId()), fred); people.put(new Integer(jim.getId()), jim); people.put(new Integer(shiela.getId()), shiela); } private static final String SESSION_CLICKS = "sessionClicks"; private static final String CONTEXT_CLICKS = "contextClicks"; private static int nextId = 10; private final Map people = new HashMap(); private Person person = new Person("John Doe", new Date().toString(), 1, 100000.0F, "Walking"); |
| <!DOCTYPE HTML PUBLIC
"-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>DWR Examples from DWR website</title> <script type='text/javascript' src='dwr/engine.js'></script> <script type='text/javascript' src='dwr/interface/Demo.js'></script> <script type='text/javascript' src='dwr/util.js'></script> <script type='text/javascript'> // Callback function for Delete method function update() { Demo.getAllPeople(fillTable); } // Functions to be passed to DWRUtil.addRows var getName = function(person) { return person.name }; var getDoB = function(person) { return person.address }; // if we return to using dates, add .toLocaleDateString() var getSalary = function(person) { return person.salary }; var getHobby = function(person) { return person.hobby}; var getEdit = function(person) { return '<input type="button" value="Edit" onclick="readPerson('+person.id+')"/>'; }; var getDelete = function(person) { return '<input type="button" value="Delete" onclick="deletePerson('+person.id+', \''+person.name+'\')"/>'; }; // Callback function for getAllPeople method // The table is reconstructed function fillTable(people) { DWRUtil.removeAllRows("peoplebody"); DWRUtil.addRows("peoplebody", people, [ getName, getDoB, getSalary, getHobby, getEdit, getDelete ]) } // Event handler to be called when Edit button is clicked function readPerson(id) { Demo.getPerson(fillForm, id); } // Event handler to be called when Delete button is clicked function deletePerson(personid, name) { if (confirm("Are you sure you want to delete " + name + "?")) { Demo.deletePerson(update, { id:personid }); } } // Event handler to be called when Save button is clicked function writePerson() { DWRUtil.getValues(person); Demo.addPerson(update, person); } var person = { id:-1, name:null, address:null, salary:null, hobby:null }; // Event handler to be called when Clear button is clicked function clearPerson() { person = { id:-1, name:null, address:null, salary:null, hobby:null }; DWRUtil.setValues(person); } // Callback function for readPerson method function fillForm(aperson) { person = aperson; DWRUtil.setValues(person); } // Function that will be called when page is loaded function init() { DWRUtil.useLoadingMessage(); update(); } // Set init function to be called when page is loaded window.onload = init; </script> </head> <body> <h1>Dynamically Editing a Table</h1> <p>This demo stores the list of people in your session, so the editing relies on <b>Cookies</b>. DWR can use application, session and request scope to store beans</p> <h2>Demo</h2> <table border="1" class="rowed"> <thead> <tr> <th>Name</th> <th>Address</th> <th>Salary</th> <th>Hobby</th> <th colspan="2">Actions</th> </tr> </thead> <tbody id="peoplebody"> </tbody> </table> <h4>Edit Person</h4> <table> <tr> <td>ID:</td> <td><span id="id">-1</span></td> </tr> <tr> <td>Name:</td> <td><input id="name" type="text"/></td> </tr> <tr> <td>Salary:</td> <td><input id="salary" type="text"/></td> </tr> <tr> <td>Address:</td> <td><input type="text" id="address"/></td> </tr> <tr> <td>Hobby:</td> <td><input type="text" id="hobby"/></td> </tr> <tr> <td colspan="2" align="right"> <input type="button" value="Save" onclick="writePerson()"/> <input type="button" value="Clear" onclick="clearPerson()"/> </td> </tr> </table> </body> </html> |

In this exercise, you will build and run
another DWR sample application called "Dynamically
Populating a
Selection List".

