I must admit – I’m neither very experienced in the field of user interfaces nor really familiarised with all that JavaScript, jQuery stuff. I was always thinking of myself more as a service layer guy.
Nevertheless, I wanted to improve my knowledge, so I decided to learn more about something called DWR (Direct Web Remoting). I’ve heard about it few years ago but never actually passed the “yeah, I’ve heard about it” stage.

This will be very basic introduction, so I’m afraid that for those of you who know what DWR is and used it, this article won’t introduce nothing new.

XMLHttpRequest

Up to know I’ve always thought that if the JavaScript have its XMLHttpRequest object, it can send asynchronous requests to the server – so this is basically the AJAX. Who needs anything more?

Well, it seems that the XMLHttpRequest is quite low-level and as usual, more high-level tools and frameworks arose. DWR is one of such frameworks, but it goes even further. Not only you don’t have to deal with those low-level XMLHttpRequest objects, but you can use your Java objects directly in the JavaScript code.

DWR creates a JavaScript’s proxy for your Java methods and sends the request to invoke particular method asynchronously – expecting the result in the callback method. You can easily define what Java methods should be accessible from the JS using the dwr.xml configuration file you’ll see in a moment.

Server side

Take a look at this Java code – it’s just a regular class with two methods. Let’s pretend it’s a Dice Roller service, so it allows you to roll a dice (rollDice()) and to scream at the dealer if you’re not happy with the resulting pips (scream(String).)

package com.piotrnowicki.dwr;
    
import java.util.Random; 
import java.util.logging.Level; 
import java.util.logging.Logger;

/** Simulates a 6-sided dice roll. Also allows to scream if you're not
 * satisfied with the result. 
 * 
 * @author PiotrNowicki 
 **/ 
 public class DiceRollService {

    private Logger log = Logger.getLogger(DiceRollService.class.getName());

    /**
     * Just for inspection when the object is created.
     */
    public DiceRollService() {
        log.info("Constructing an object");
    }

    /**
     * Roll a 6-sided dice.
     * 
     * @return rolled pips.
     */
    public Integer rollDice() {
        return new Random().nextInt(6) + 1;
    }

    /**
     * Scream (e.g. at the dealer) given sentence. Shows how you can 
     * deal with parameters in DWR.
     * 
     * @param msg
     *            what to be screamed.
     */
    public void scream(String msg) {
        log.log(Level.INFO, "Screaming: {0}", msg);
    }
}

This is a class which methods we want to be accessible directly from the JS (embedded on our user-interface HTML page). To achieve this, we need to inform the DWR about the existence of the DiceRollService class. We do this by creating a dwr.xml file in webapp/WEB-INF/ (just next to the web.xml):

<!DOCTYPE dwr 
  PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 3.0//EN" 
  "http://getahead.org/dwr/dwr30.dtd">
<dwr>
    <allow>
        <create creator="new" 
                javascript="JSDiceRollService" 
                scope="application">
            <param name="class" 
                   value="com.piotrnowicki.dwr.DiceRollService" />
        </create>
    </allow>
</dwr>

The above excerpt tells DWR to create an instance of DiceRollService using the new Java operator (you can as well use static method to obtain your class instance, access bean registered in the Spring Framework and so on – read this page for more details.)

This newly created object will be named JSDiceRollService and it’ll be bound to the application scope (similarly as in Servlets, we have: application, session, page and request scope.)

The last thing we need is to create a DWR Servlet which will be responsible for registering our classes / methods. It will make them accessible for the DWR client or RESTful client. We do that by creating a web.xml that looks somewhat like this:

<?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" 
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
         http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" 
         version="3.0">
    <servlet>
        <servlet-name>dwr-invoker</servlet-name>
        <servlet-class>
            org.directwebremoting.servlet.DwrServlet
        </servlet-class>
        <init-param> 
            <param-name>jsonpEnabled</param-name>
            <param-value>true</param-value>
        </init-param>
        <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>
</web-app>

Notice the /dwr/* url-pattern has been bound to the DWR Servlet – this is the entry-point to the server-side DWR. You don’t really need the jsonpEnabled init-param, but in a moment I’ll show you why it can be useful.

The debug param allows you to see the test page when you enter the DWR Servlet URL, e.g.: http://localhost:8080/DWRTest/dwr (DWRTest is an application context.)

I’m not aware if this can be configured using the Servlets 3.0 @WebServlet annotation, but as I don’t see much added value using it here, I won’t even try digging it.

And that’s pretty much all there is – you’ve created your business logic class in pure Java, instructed DWR that it should create a JS proxy for your class and, finally, you’ve defined and configured the server-side DWR Servlet.

Now it’s time to go to the client layer.

Client Side

First thing you can check after you run your application, is to enter the already mentioned DWR test page (e.g. http://localhost:8080/DWRTest/dwr) and see if you can access your Java methods from it. You can observe that your Java object will be created as many times as appropriate, depending on your scope attribute value in dwr.xml. Every object instantiation is logged, so you can inspect your server logs if you want to check it.

However, let’s go to some more interesting stuff – accessing your Java objects from an arbitrary JavaScript code. Take a look at the code below (it’s called index.jsp and it’s located directly in the webapp/ directory):

<!DOCTYPE html 
  PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
  <html> 
    <head> 
      <title>DWR Test</title> 
    </head> 
    <body>
      <script type="text/javascript" 
              src="dwr/engine.js"></script> 
      <script type="text/javascript" 
              src="dwr/interface/JSDiceRollService.js"></script>
      <input type="button" value="Roll a dice" onclick="clicked()" /> 
      <div id="diceRes"></div> 

      <script type="text/javascript">
        function clicked() { 
          JSDiceRollService.rollDice({ callback : function(str) { 
            document.getElementById("diceRes").innerHTML = str; 
          } }); 
        } 

        // Taken from kdenney's post: 
        //        http://stackoverflow.com/a/155265/920607 
        function keyPressed(e) { 
          if (typeof e == 'undefined' && window.event) { 
            e = window.event; 
          } 

          if (e.keyCode == 13) { 
            JSDiceRollService.scream(
                 document.getElementById("shoutField").value, { 
                   callback : function(str) { 
                     alert("Check your Java server logs"); 
                   } 
                 }
            ); 
          } 
        }

      </script> 
      <br /> <br /> 
      Type any text to shout in Java System.out.println 
      (ENTER sends the request): 
      <input id="shoutField" 
             type="text" 
             value="You bastard!" 
             onkeypress="keyPressed(event)" /> 
    </body>
  </html>

It’s not the most elegant and prettiest HTML + JavaScript code in the world, but for this exemplary purpose – it does it work. I’m not sure if it will even pass the W3C validator, but that’s not the point now.

Take a look at the <script> imports at the beginning of the <body>. It’s value refers to our DWR Servlet URL, so those scripts are generated by DWR for you. Notice that you’ve also included a JavaScript code for DWR generated proxy – dwr/interface/JSDiceRollService.js.

Also, be very careful with the order of imports. The engine.js script import must occur before your JS proxy import.

This simple page shows you:

  • a button which will invoke DiceRollService#rollDice() method and print the result in div with ID = diceRes,
  • an input field that after you hit ENTER, will invoke DiceRollService#scream(msg) where the msg is text typed into the field.

That’s basically it – you should be able to successfully send an asynchronous HTTP request which will invoke one of your Java methods using XMLHttpRequest under the hood. Pretty neat, huh?

REST

One of the additional features of the DWR is the support for REST’style requests / responses. If you access the following URL: http://localhost:8080/DWRTest/dwr/jsonp/JSDiceRollService/rollDice (of course, substituting the appropriate parts like protocol, host, port and application context) you can access the DiceRollService#rollDice method as well. The response will be in the JSONP format and it should look something like this:

{ "reply":5 }

Summary

I didn’t test any reverse Ajax support, as I think I’ll write a separate post about it – just when I’ll try to do some tests with Server Sent Events and Web Sockets – so stay tuned!

For the first time I think I understood what the DWR really does and how it can improve your development.

However, I’m curious what will happen if the boundary of the service layer is an EJB. The EJB container is responsible for creation of the EJB’s and the EJB support for DWR doesn’t seem to be stable yet.
What about the CDI and its scopes? Perhaps the @Produces CDI method and scope = static in DWR will do the work?

What is the place of DWR in JSF and managed beans world? What about the JSF managed beans scopes and DWR scopes – how do they comply?

There are a lot of questions I don’t know answer at this point. Nevertheless, I think it’s just a regular matter of picking the right tool to do the job.