Você está na página 1de 10

DWR: Easy AJAX for JAVA

DWR is a RPC library which makes it easy to call Java functions from JavaScript and to call JavaScript functions from Java (a.k.a Reverse Ajax). It has a large user-base, active mailing list and has been used in many projects. DWR has a number of features like call batching, marshalling of virtually any data-structure between Java and Javascript (including binary file uploading and downloading), exception handling, advanced CSRF protection and deep integration with several Java server-side technologies like Spring and Guice. The first diagram shows how DWR can alter the contents of a selection list as a result of some Javascript event.

Reverse Ajax (available since DWR version 2.0) allows Java code running on the server to find out what clients are viewing certain pages, and to send to them JavaScript, generated either manually or using a Java API. These JavaScript generating APIs generally match a client-side APIs.

DWR consists of two main parts:


A Java Servlet running on the server that processes requests and sends responses back to the browser. JavaScript running in the browser that sends requests and can dynamically update the webpage.

DWR works by dynamically generating Javascript based on Java classes. The code does some Ajax magic to make it feel like the execution is happening on the browser, but in reality the server is executing the code and DWR is marshalling the data back and forwards. This method of remoting functions from Java to JavaScript gives DWR users a feel much like conventional RPC mechanisms like RMI or SOAP, with the benefit that it runs over the web without requiring web-browser plug-ins.

Getting Started with DWR


There are several ways to get started with DWR. We recommend the following:
1. Follow the 5 steps outlined below. 2. Download the dwr.war file and experiment with the examples. 3. Once DWR is up and running learn how to write JavaScript for DWR. DWR in 5 steps 1. Install the DWR JAR file

Download the dwr.jar file. Place it into the WEB-INF/lib directory of your web application.
2. Install the Commons Logging JAR file

DWR depends on Commons Logging. Download the commons-logging.jar and place it into the WEB-INF/lib directory of your web application.
3. Add the DWR servlet definition and mapping to your application's web.xml

Add the following lines to your web application's deployment descriptor (WEB-INF/web.xml). The <servlet> section needs to go with any existing <servlet> sections, and likewise with the <servlet-mapping> section.
<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> </servlet> <servlet-mapping> <servlet-name>dwr-invoker</servlet-name> <url-pattern>/dwr/*</url-pattern> </servlet-mapping>

4. Create the DWR configuration file (dwr.xml)

Create a new file in you web application's WEB-INF directory (alongside web.xml) named dwr.xml. A simple way to start is with something like this:
<!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 3.0//EN" "http://getahead.org/dwr/dwr30.dtd"> <dwr> <allow> <create creator="new" <param name="class" </create> <create creator="new" <param name="class" </create> </allow> </dwr>

javascript="JDate"> value="java.util.Date"/> javascript="Demo"> value="your.java.Bean"/>

The DWR config file defines what classes DWR can create and remote for use by Javascript. In the example above we are defining 2 classes that are remoted and giving the classes names in Javascript. The new creator that we used above uses the public no-args constructor that all Java Beans must have. It is also worth remembering that DWR has one restriction:

Avoid reserved JavaScript words; Methods named after reserved words are automatically excluded. Most JavaScript reserved words are also Java reserved words, so you won't be having a method called "try()" anyway. However the most common gotcha is "delete()", which has special meaning in JavaScript but not Java.

Visit the dwr.xml documentation for a detailed description of the dwr.xml file, creators and converters.
5. Take DWR for a test drive!

Go to the following URL:


http://localhost:8080/[YOUR-WEBAPP-CONTEXT]/dwr/

You should see a page showing you the classes you configured in Step 4. Having followed a link you should see an index of all the methods ready for calling - simply enter the required parameters (if parameters are required) and click the execute button. These pages are valuable debugging tools and dynamically generated by DWR when the debug init-param is set to true.
How to make use of this from your web application

We have a whole section on writing Javascript with DWR, and there are a number of examples in the sidebar that demonstrate how to dynamically alter the text in web pages, update lists, manipulate forms and perform live table editing. Each has a description of how it works. Another way to get started is to look at the source from the pages that you just viewed:
3

Go to http://localhost:8080/[YOUR-WEBAPP-CONTEXT]/dwr/ and click on your class View source and find the line that executes the method that you are interested in. Paste the text into an HTML or JSP page in your web-app. Include links to the DWR JavaScript files that make the magic happen:

<script src='/[YOUR-WEBAPP-CONTEXT]/dwr/interface/[YOUR-SCRIPT].js'></script> <script src='/[YOUR-WEBAPP-CONTEXT]/dwr/engine.js'></script>

You can omit the /[YOUR-WEBAPP-CONTEXT]/ section and use relative paths in your web pages if you wish. Next Step: Learn about writing Javascript that interacts with DWR.

Remoting Options
DWR provides two remoting options: 1) DWR native - By default DWR uses a superset of JSON that we like to call JavaScript. This was initially done to cope with recursive data, XML objects, etc., which the original JSON specifications had a difficult time dealing with. In this remoting mode DWR's client side engine (engine.js) handles preparing and executing the call to the server. Most of the documentation on this site deals with DWR native. 2) JSON/JSONP - In DWR 3 a JSON/JSONP RESTian API has been added that allows users to access Java services exposed to DWR via an URL: http://example.com/dwr/jsonp/ClassName/methodName/param1/param2 DWR will reply with a JSON structure containing the result of calling ClassName.methodName("param1", "param2"); This allows for seamless integration with JavaScript toolkits (DOJO, ExtJs, jQuery, etc.) while eliminating the need for the developer to convert the return types of their exposed Java methods into JSON manually - DWR's server engine handles this.

Getting Started with JSON/JSONP


In order to enable JSON/JSONP you must add the jsonpEnabled init-param to your DWR servlet definition in your web.xml. This parameter activates the JSON/JSONP service.
... <servlet> <display-name>DWR Servlet</display-name> <servlet-name>dwr-invoker</servlet-name> <description>Direct Web Remoter Servlet</description> <servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class> <init-param> <param-name>jsonpEnabled</param-name> <param-value>true</param-value> </init-param> ...

For an example of the JSON/JSONP functionality you can view this demo which uses JQuery. A similar example using DOJO is also avaiable. Both of these examples are available for download in our dwr.war.

Remote calls with DWR - handling the asynchronous nature of AJAX with callbacks
DWR generates JavaScript proxy functions that are similar to the Java methods exposed using the <create ... > element, in your dwr.xml file. The biggest challenge in creating a remote interface to match Java code across AJAX is the (usually) asynchronous nature of AJAX, compared to the synchronous nature of a normal Java call. DWR solves this problem by introducing a callback function that is called when the data is returned from the server. There are 2 recommended ways to make a call using DWR. Either by appending a call options object or a callback function to the parameter list.

1. Call Options Object


The recommended syntax is to make use of a call options object that specifies the callback function and other options. Suppose we have a Java method that looks like this:
public class Remote { public String getData(int index) { ... } }

We can use this from Javascript as follows:


<script type="text/javascript" src="[WEBAPP]/dwr/interface/Remote.js"> </script> <script type="text/javascript" src="[WEBAPP]/dwr/engine.js"> </script> ... Remote.getData(42, { callback:function(str) { alert(str); } });

'42' is just the parameter to the getData() Java function - see above. DWR will always pass a JavaScript version (serialized using DWR's converters) of the object returned from the remoted Java method as the first parameter of the callback. In this case str will contain the String that getData returns. This method has some advantages: Depending on your style it may be easier to read, but more importantly it allows you to specify extra call options.

Timeouts and Handling Errors

In addition to the callback function you can also specify a timeout, errorHandler, and several other call options. For example:
Remote.getData(42, { callback:function(str) { alert(str); }, timeout:5000, errorHandler:function(message) { alert("Oops: " + message); } });

2. Simple Callback Functions


An alternative approach is to simply append a callback function to your parameter list:
function handleGetData(str) { alert(str); } Remote.getData(42, handleGetData);

Alternatively you can use the all-in-one-line version:


Remote.getData(42, function(str) { alert(str); });

Creating JavaScript objects to match Java objects


Suppose you have exposed the following Java method, with a Person Java Bean as a parameter:
public class Remote { public void doSomethingWithPerson(Person p) { // Some Java code that does something with Person p. } }

And Person looks like this:


public Person { private String name; private int age; private Date[] appointments; // getters and setters ... }

Then you can call this from Javascript like this:


var myJSPerson = { name:"Fred Bloggs", age:42, appointments:[ new Date(), new Date("1 Jan 2008") ] }; Remote.doSomethingWithPerson(myJSPerson);

Any fields missing from the JavaScript representation will be left unset in the Java version.
6

Since the doSomethingWithPerson method returns 'void' we do not need to use a callback and can just leave it out. If you want to be notified of the completion of a server-side method that returns void then you can include a callback method. Obviously DWR will not pass any data to it.

Exemple 1 - Demonstration of how to dynamically update a web-page with text fetched from a web server
When you click on the "Send" button the browser calls the onclick event, which calls the update() function:
function update() { var name = dwr.util.getValue("demoName"); $.post("../../../dwr/jsonp/Demo/sayHello/" + name, {}, function(data) { dwr.util.setValue("demoReply", data.reply); }, "jsonp"); } } dwr.util.getValue()

is a utility to get the value of any element, in this case an input field, but it could be a

div or a select box. The next line is using the jQuery post function to make an asynchronous call to the server utilizing DWR's JSON capabilities. The first parameter is the URL for DWR's JSON service. The URL can be broken down as follows:

../../../dwr/ - The path to the DWR servlet. jsonp - Tells DWR to use JSONP. Demo - The name of the class you have exposed to DWR. sayHello - The name of the method you would like to call on the exposed class

The third parameter is the callback function and will be invoked when the remote call returns. The first parameter of the callback function (in this case "data") will contain the data returned from the remote call. The result of the $.post function is that DWR will call the Demo.sayHello() Java method:
public String sayHello(String name) { return "Hello, " + name; }

When the callback function is reached we call dwr.util.setValue() which is a utility that takes the data you pass in the second parameter and works out how to fit it to go into the HTML element specified by id in the first parameter. This function is one of several neat Javascript utilities that make working with DWR much easier.

HTML source:
<p> Name: <input type="text" id="demoName"/> <input value="Send" type="button" onclick="update()"/> <br/> Reply: <span id="demoReply"></span> </p>

Javascript source:
function update() { var name = dwr.util.getValue("demoName"); $.post("../../../dwr/jsonp/Demo/sayHello/" + name, { }, function(data) { dwr.util.setValue("demoReply", data.reply); }, "jsonp"); } }

Java source:
package org.getahead.dwrdemo.simpletext; public class Demo { public String sayHello(String name) { return "Hello, " + name; } }

web.xml
Note: In order to enable JSON/JSONP you must add the following init-param to your DWR servlet definition in your web.xml (This is a snippet and is not a complete web.xml).
... <servlet> <servlet-name>dwr-invoker</servlet-name> <display-name>DWR Servlet</display-name> <description>Direct Web Remoter Servlet</description> <servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class> <init-param> <param-name>jsonpEnabled</param-name> <param-value>true</param-value> </init-param> ...

dwr.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 2.0//EN" "http://getahead.org/dwr/dwr20.dtd"> <dwr> <allow> <create creator="new" javascript="Demo"> <param name="class" value="org.getahead.dwrdemo.simpletext.Demo"/> </create> </allow> </dwr>

Exemple 2 - how to dynamically update a web-page with the contents of a page retrieved from the server
Normally the code called by DWR is a simple POJO that knows nothing about the web. In this demonstration we want to find out about the web server so we use the WebContext class to get access to the ServletContext. This example is similar to the dynamic text example, the big difference is that we are using this HTML page, and the WebContext class to allow us to get it. There are no iframes used in this example - just DWR and a humble div. The Java code looks like this:
public String getInclude() { WebContext wctx = WebContextFactory.get(); return wctx.forwardToString("/simpletext/forward.html"); }

There is one other difference. In the previous example we were working with plain text. Here we are working with HTML. To help protect you from XSS attacks DWR automatically escapes HTML characters, however sometimes (like in this case) we know that we have HTML, and that it is safe. So the Javascipt code looks like this:
function forward() { Demo.getInclude(function(data) { dwr.util.setValue("forward", data, { escapeHtml:false }); }); }

The third parameter is the options parameter which is present on a number of DWR functions. It allows you to customize how the function acts. In this case we are asking DWR to not escape and HTML in the passed string.

HTML source:
<p> <input value="Include Page" type="button" onclick="forward()"/><br/> Included Page: </p> <div id="forward"></div>

Javascript source:
function forward() { Demo.getInclude(function(data) { dwr.util.setValue("forward", data, { escapeHtml:false }); }); }

Java source:
package org.getahead.dwrdemo.simpletext; import import import import java.io.IOException; javax.servlet.ServletException; org.directwebremoting.WebContext; org.directwebremoting.WebContextFactory;

public class Demo { public String getInclude() throws ServletException, IOException {

WebContext wctx = WebContextFactory.get(); return wctx.forwardToString("/simpletext/forward.html"); } }

dwr.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 2.0//EN" "http://getahead.org/dwr/dwr20.dtd"> <dwr> <allow> <create creator="new" javascript="Demo"> <param name="class" value="org.getahead.dwrdemo.simpletext.Demo"/> </create> </allow> </dwr>

10

Você também pode gostar