Tag Archives: Ajax for Java developers

[repost ] Ajax for Java developers: Exploring the Google Web Toolkit

Summary: The recently released Google Web Toolkit (GWT) is a comprehensive set of APIs and tools that lets you create dynamic Web applications almost entirely in Java™ code. Philip McCarthy returns to his popular Ajax for Java developers series to show you what GWT can do and help you decide whether it’s right for you. (Note: You can now download an updated ZIP file containing the article source code.)

View more content in this series

The GWT (see Resources) takes an unusual approach to Web application development. Rather than employing the normal separation of client-side and server-side codebases, GWT provides a Java API that lets you create component-based GUIs and then compile them for display in the user’s Web browser. Using GWT is far closer to developing with Swing or SWT than the usual experience of Web application development, and it tries to abstract away the HTTP protocol and the HTML DOM model. Indeed, the fact that the application ends up being rendered in a Web browser feels almost incidental.

GWT achieves these feats through code generation, with GWT’s compiler generating JavaScript from your client-side Java code. It supports a subset of the java.lang and java.util packages, along with the APIs that GWT itself provides. A compiled GWT application consists of fragments of HTML, XML, and JavaScript. However, these are pretty much indecipherable, and the compiled application is best regarded as a black box — GWT’s equivalent of Java bytecode.

In this article, I’ll run through creating a simple GWT application to fetch a weather report from a remote Web API and display it in the browser. On the way, I’ll briefly cover as many of GWT’s capabilities as possible, and I’ll mention some of the potential problems you’ll come across.

Starting simple

Listing 1 shows the Java source code of pretty much the simplest possible application you can make using GWT:
Listing 1. Simplest GWT example

public class Simple implements EntryPoint {

   public void onModuleLoad() {
     final Button button = new Button("Say 'Hello'");

     button.addClickListener(new ClickListener() {
        public void onClick(Widget sender) {
        Window.alert("Hello World!");
        }
     });

     RootPanel.get().add(button);
   }
}

 

This looks a lot like GUI code you might have written in Swing, AWT, or SWT. As you can probably guess, Listing 1 creates a button that displays a “Hello World!” message when you click it. The button is added to the RootPanel, a GWT wrapper around the body of an HTML page. Figure 1 shows the application in action, running inside GWT Shell. The Shell is a debugging hosting environment, incorporating a simple Web browser, that’s included in the GWT SDK.
Figure 1. Simplest GWT example in action

Back to top

Building the Weather Reporter application

I’m going to use GWT to create a simple Weather Reporter application. The application’s GUI presents the user with an input box for entering a ZIP code and a choice of Celsius or Fahrenheit to represent temperatures. When the user clicks on a Submit button, the application uses Yahoo!’s free Weather API to obtain an RSS-formatted report for the chosen location. The HTML portion of this document is extracted and displayed to the user.

GWT applications are packaged as modules and must conform to a specific structure. A configuration file — named module-name.gwt.xml — defines the class that acts as the application’s entry point and indicates whether resources should be inherited from other GWT modules. You must place the configuration file in the application’s source package structure at the same level as a package named client, where all the client-side Java code resides, and a directory named public, which contains project Web resources such as images, CSS, and HTML. Finally, the public directory must include an HTML file with a meta tag containing the module’s qualified name. GWT’s run-time JavaScript library uses this file to initialize the application.

GWT’s applicationCreator generates this basic structure for you, given the name of your entry-point class. So calling
applicationCreator developerworks.gwt.weather.client.Weather generates a project outline I can use as a starting point for the Weather Reporter application. The source download for the application includes an Ant buildfile containing some useful targets for working with a GWT project conforming to this structure (see Download).

Developing the basic GUI

First, I’ll develop the basic layout of the application’s user-interface widgets, without adding any behavior. The superclass of pretty much everything you can render in a GWT UI is the Widget class. Widgets are always contained in Panels, which are themselves Widgets and therefore can be nested. Different types of panels offer different layout behaviors. So, a GWT Panelplays a similar role to that of a Layout in AWT/Swing or a Box in XUL.

All widgets and panels must ultimately be attached to the Web page that hosts them. As you saw in Listing 1, you can attach them directly to the RootPanel. Alternatively, you can use RootPanel to obtain references to HTML elements identified by their IDs or classnames. In this case, I’ll use two separate HTML DIV elements named input-container and output-container. The first contains the UI controls for the Weather Reporter; the second displays the weather report itself.

Listing 2 shows the code needed to set up the basic layout; it should be self-explanatory. The HTML widget is simply a container for HTML markup. This is where the HTML output from the Yahoo! weather feed is displayed. All of this code goes inside theWeather class’s onModuleLoad() method, provided by the EntryPoint interface. This method is invoked when a Web page embedding the Weather module is loaded into a client Web browser.
Listing 2. Layout code for the Weather Reporter

public void onModuleLoad() {

   HorizontalPanel inputPanel = new HorizontalPanel();

   // Align child widgets along middle of panel
   inputPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);

   Label lbl = new Label("5-digit zipcode: ");
   inputPanel.add(lbl);

   TextBox txBox = new TextBox();
   txBox.setVisibleLength(20);

   inputPanel.add(txBox);

   // Create radio button group to select units in C or F
   Panel radioPanel = new VerticalPanel();

   RadioButton ucRadio = new RadioButton("units", "Celsius");
   RadioButton ufRadio = new RadioButton("units", "Fahrenheit");

   // Default to Celsius
   ucRadio.setChecked(true);

   radioPanel.add(ucRadio);
   radioPanel.add(ufRadio);

   // Add radio buttons panel to inputs
   inputPanel.add(radioPanel);

   // Create Submit button
   Button btn = new Button("Submit");    

   // Add button to inputs, aligned to bottom
   inputPanel.add(btn);
   inputPanel.setCellVerticalAlignment(btn,
      HasVerticalAlignment.ALIGN_BOTTOM);

   RootPanel.get("input-container").add(inputPanel);

   // Create widget for HTML output
   HTML weatherHtml = new HTML();

   RootPanel.get("output-container").add(weatherHtml);
}

 

Figure 2 shows the layout rendered in the GWT Shell:
Figure 2. Basic GUI layout

Adding styling with CSS

The rendered Web page is looking pretty dull, so it would benefit from some CSS styling rules. You can take a couple of approaches to styling a GWT application. First, by default, each widget has a CSS classname of the form project-widget. For example, gwt-Button and gwt-RadioButton are two of the core GWT widgets’ classnames. Panels are generally implemented as a mess of nested tables and don’t have default classnames.

The default classname-per-widget-type approach makes it easy to style widgets uniformly across your application. Of course, normal CSS selector rules apply, and you can exploit them to apply different styles to the same widget type depending on its context. For even more flexibility, you can replace or augment widgets’ default classnames on an ad-hoc basis by calling theirsetStyleName() and addStyleName() methods.

Listing 3 combines these approaches to apply styles to the Weather Reporter application’s input panel. The weather-input-panel classname is created in Weather.java with a call to inputPanel.setStyleName("weather-input-panel");.
Listing 3. Applying CSS styles to the Weather Reporter application’s input panel

/* Style the input panel itself */
.weather-input-panel {
   background-color: #AACCFF;
   border: 2px solid #3366CC;
   font-weight: bold;
}

/* Apply padding to every element within the input panel */
.weather-input-panel * {
   padding: 3px;
}

/* Override the default button style */
.gwt-Button {
   background-color: #3366CC;
   color: white;
   font-weight: bold;
   border: 1px solid #AACCFF;
}

/* Apply a hover effect to the button */
.gwt-Button:hover {
   background-color: #FF0084;
}

 

Figure 3 shows the application again, with these styles in place:
Figure 3. Input panel with styles applied
Input panel with styles applied

Adding client-side behavior

Now that the basic layout and styling of the application is done, I can start applying some client-side behavior. You use the familiar Listener pattern to perform event handling in GWT. GWT provides Listener interfaces for mouse events, keyboard events, change events, and so forth, as well as several adapter and helper classes for added convenience.

Generally, you add event listeners using the anonymous inner-class idiom familiar to Swing programmers. However, the first parameter of all GWT’s Listener methods is the event’s sender, usually the widget that the user just interacted with. This means that you can attach the same Listener instance to multiple widgets where necessary; you can use the sender parameter to determine which of them fired the event.

Listing 4 shows the implementation of two event listeners in the Weather Reporter application. A click handler is added to theSubmit button, and a keyhandler is added to the TextBox. Either clicking on the Submit button or pressing the Enter key when the TextBox has focus causes the associated handler to call the private validateAndSubmit() method. In addition to the code in Listing 4, txBox and ucRadio have become instance members of the Weather class so that they can be accessed from the validation method.
Listing 4. Adding client-side behavior

// Create Submit button, with click listener inner class attached
Button btn = new Button("Submit", new ClickListener() {

   public void onClick(Widget sender) {
      validateAndSubmit();
   }
});

// For usability, also submit data when the user hits Enter 
// when the textbox has focus
txBox.addKeyboardListener(new KeyboardListenerAdapter(){

   public void onKeyPress(Widget sender, char keyCode, int modifiers) {

      // Check for Enter key
      if ((keyCode == 13) && (modifiers == 0)) {
         validateAndSubmit();
      }        
   }      
});

 

Listing 5 shows the implementation of the validateAndSubmit() method. It’s fairly simple, relying on a ZipCodeValidatorclass that encapsulates the validation logic. If the user has not entered a valid five-digit ZIP code, validateAndSubmit()displays an error message in an alert box, expressed in the GWT world as a call to Window.alert(). If the ZIP code is valid, then both it and the user’s choice of Celsius or Fahrenheit units are passed to the fetchWeatherHtml() method, which I’ll get to a little later.
Listing 5. validateAndSubmit logic

private void validateAndSubmit() {

   // Trim whitespace from input
   String zip = txBox.getText().trim();

   if (!zipValidator.isValid(zip)) {
     Window.alert("Zip-code must have 5 digits");
     return;
   }

   // Disable the TextBox
   txBox.setEnabled(false);

   // Get choice of celsius/fahrenheit
   boolean celsius = ucRadio.isChecked();
   fetchWeatherHtml(zip, celsius);
}

 

Back to top

Client-side debugging with the GWT Shell

I’m going to sidetrack a little here to mention that the GWT Shell has JVM hooks that let you debug your client-side code in a Java IDE. You can interact with your Web UI and step through the Java code that represents the corresponding JavaScript executed on the client. This is an important ability because debugging the generated JavaScript on the client side is basically a nonstarter.

It’s easy to configure an Eclipse debug task to launch the Shell via the com.google.gwt.dev.GWTShell class. Figure 4 shows Eclipse paused at a breakpoint in the validateAndSubmit() method, following a click on the Submit button:
Figure 4. Eclipse debugging client-side GWT code

Back to top

Communicating with server-side components

Now the Weather Reporter application can collect and verify user input. The next step is to fetch data from the server. In normal Ajax development, this would entail calling a server-side resource directly from JavaScript and receiving data back encoded as JavaScript Object Notation (JSON) or XML. GWT abstracts this communication process behind its own remote procedure call (RPC) mechanism.

In GWT terminology, client code communicates with services running on the Web server. The RPC mechanism used to expose these services has similarities to the approach Java RMI uses. This means you only need to write the server-side implementation of your service and a couple of interfaces. Code generation and reflection take care of client stubs and server-side skeleton proxies.

Accordingly, the first step is to define an interface for the Weather Reporter service. This interface must extend the GWTRemoteService interface, and it contains the signatures of service methods that should be exposed to the GWT client code. Because RPC calls in GWT are between JavaScript code and Java code, GWT incorporates an object-serialization mechanism to mediate arguments and return values across the language divide (see the Serializable types sidebar to see what you can use).

Serializable types

A brief rundown of serializable types under GWT goes like this:

  • Primitives (such as int) and the primitive wrapper classes (such as Integer) are serializable.
  • String and Date are serializable.
  • Arrays of serializable types are themselves serializable.
  • User defined classes are serializable if all of their nontransient members are serializable and they implement GWT’s IsSerializableinterface.
  • Collection classes can be used in conjunction with Javadoc annotations that state the serializable type that they contain.

Because client code is limited to the small subset of Java classes implemented by GWT anyway, these serializable types provide fairly comprehensive coverage.

With a service interface defined, the next step is to implement it in a class that extends GWT’s RemoteServiceServlet class. As the name suggests, this is a specialization of the Java language’sHttpServlet, so it can be hosted on any servlet container.

One GWT peculiarity worth mentioning here is that the service’s remote interface must live in your application’s client package because it needs to be incorporated in the JavaScript generation process. However, because the server-side implementation class references the remote interface, a Java compile-time dependency now exists between server-side and client code. My solution to this is to place the remote interface into a common subpackage of client. I then include common in the Java build but exclude the rest of theclient package. This prevents class files from being generated from client code that only needs to be converted to JavaScript. A more elegant solution might be to split package structure across two source directories for client-side and server-side code and to duplicate common classes into both directories.

Listing 6 shows the remote service interface used in the Weather Reporter application: WeatherService. It takes a ZIP code and a Celsius/Fahrenheit flag as input and returns a String containing an HTML weather description. Listing 6 also shows the outline ofYahooWeatherServiceImpl, which uses the Yahoo! weather API to obtain an RSS weather feed for the given ZIP code and extracts an HTML description from it.
Listing 6. Remote WeatherService interface and partial implementation

public interface WeatherService extends RemoteService {

   /**
    * Return HTML description of weather
    * @param zip zipcode to fetch weather for
    * @param isCelsius true to fetch temperatures in celsius, 
    * false for fahrenheit
    * @return HTML description of weather for zipcode area
    */
   public String getWeatherHtml(String zip, boolean isCelsius) 
      throws WeatherException;
} 

public class YahooWeatherServiceImpl extends RemoteServiceServlet
   implements WeatherService {

   /**
    * Return HTML description of weather
    * @param zip zipcode to fetch weather for
    * @param isCelsius true to fetch temperatures in celsius, 
    * false for fahrenheit
    * @return HTML description of weather for zipcode area
    */
   public String getWeatherHtml(String zip, boolean isCelsius) 
      throws WeatherException {

     // Clever programming goes here
   }
}

 

Things begin to diverge from the standard RMI approach at this point. Because Ajax calls from JavaScript are asynchronous, some extra work is needed to define an asynchronous interface that the client code uses to invoke the service. The asynchronous interface’s method signatures differ from those of the remote interface, so GWT relies on Magical Coincidental Naming. In other words, no static compile-time relationship exists between the asynchronous interface and the remote interface, but GWT figures it out through naming conventions. Listing 7 shows the asynchronous interface for WeatherService:
Listing 7. Asynchronous interface for WeatherService

public interface WeatherServiceAsync {

   /**
    * Fetch HTML description of weather, pass to callback
    * @param zip zipcode to fetch weather for
    * @param isCelsius true to fetch temperatures in celsius,
    * false for fahrenheit
    * @param callback Weather HTML will be passed to this callback handler
    */
   public void getWeatherHtml(String zip, boolean isCelsius, 
      AsyncCallback callback);
}

 

As you can see, the general idea is to create an interface called MyServiceAsync and to provide counterparts for each method signature, removing the return type and adding an extra parameter of type AsyncCallback. The asynchronous interface must also live in the same package as the remote interface. The AsyncCallback class has two methods: onSuccess() andonFailure(). If the call to the service is successful, then onSuccess() is invoked with the return value of the service call. If the remote call fails, onFailure() is invoked and passed the Throwable generated by the service to represent the cause of the failure.

Back to top

Invoking the service from the client

With the WeatherService and its asynchronous interface in place, I can now modify the Weather Reporter client to invoke the service and handle its response. The first step is just boilerplate setup code: it creates an instance of WeatherServiceAsyncfor the Weather client to use by calling GWT.create(WeatherService.class) and downcasting the object it returns. Next, theWeatherServiceAsync must be cast to a ServiceDefTarget so that setServiceEntryPoint() can be called on it.setServiceEntryPoint() points the WeatherServiceAsync stub at the URL where its corresponding remote service implementation is deployed. Note that this is effectively hardcoded at compile time. Because this code becomes JavaScript deployed in a Web browser, there’s no way to look up this URL from a properties file at run time. Obviously, this constrains the portability of a compiled GWT Web application.

Listing 8 shows the setup of the WeatherServiceAsync object and then gives the implementation of fetchWeatherHtm(), which I mentioned earlier (see Adding client-side behavior):
Listing 8. Using RPC to invoke a remote service

// Statically configure RPC service
private static WeatherServiceAsync ws = 
   (WeatherServiceAsync) GWT.create(WeatherService.class);
static {
   ((ServiceDefTarget) ws).setServiceEntryPoint("ws");
}

/**
 * Asynchronously call the weather service and display results
 */
private void fetchWeatherHtml(String zip, boolean isCelsius) {

   // Hide existing weather report
   hideHtml();

   // Call remote service and define callback behavior
   ws.getWeatherHtml(zip, isCelsius, new AsyncCallback() {
      public void onSuccess(Object result) {

         String html = (String) result;

         // Show new weather report
         displayHtml(html);
      }

      public void onFailure(Throwable caught) {
         Window.alert("Error: " + caught.getMessage());
         txBox.setEnabled(true);
       }
   });
}

 

The actual call to the service’s getWeatherHtml() is straightforward to implement, with an anonymous callback handler class simply passing the server’s response on to a method that displays it.

Figure 5 shows the application in action, displaying a weather report that has been fetched from Yahoo!’s weather API:
Figure 5. The Weather Reporter application displaying a report fetched from Yahoo!

Back to top

The need for server-side validation

GWT’s conflation of client-side and server-side code is inherently dangerous. Because you program everything in the Java language, with GWT’s abstraction concealing the client/server split, it’s easy to be misled into thinking that your client-side code can be trusted at run time. This is a mistake. Any code that executes in a Web browser can be tampered with, or bypassed completely, by a malicious user. GWT provides a high level of obfuscation that mitigates the problem to a degree, but a secondary point of attack remains: any HTTP traffic that travels between your GWT client and its services.

Suppose I’m an attacker wishing to exploit weaknesses in the Weather Reporter application. Figure 6 shows Microsoft’s Fiddler tool intercepting a request from the Weather Reporter client to the WeatherService running on the server. Once intercepted, Fiddler lets you alter any part of the request. The highlighted text shows that I’ve found where the ZIP code that I specified is encoded in the request. I can now alter this to anything I like, perhaps from “10001” to “XXXXX.”
Figure 6. Using Fiddler to bypass client-side validation

Now, suppose some naive server-side code in the YahooWeatherServiceImpl calls Integer.parseInt() on the ZIP code. After all, the ZIP code must have passed the validation check that was incorporated into Weather‘s validateAndSubmit()method, right? Well, as you’ve seen, this check has been subverted and now a NumberFormatException is thrown.

In this case, nothing terrible happens, and the attacker simply sees an error message in the client. However, the potential exists for a whole class of attacks against GWT applications dealing in more sensitive data. Imagine the ZIP code was instead a customer ID number in an order-tracking application. Intercepting and changing this value could expose sensitive financial information about other customers. Anyplace that a database query uses a value, the same approach allows the possibility of a SQL injection attack.

None of this should be rocket science to anyone who has worked with Ajax applications before. You simply need to double-check any input values by revalidating them on the server side. The key is remembering that some of the Java code you write in your GWT application is essentially untrustworthy at run time. This GWT cloud does have a silver lining, however. In the Weather Reporter application, I’ve already written a ZipCodeValidator for use on the client, so I can simply move it into myclient.common package and reuse the same validation on the server side. Listing 9 shows this check incorporated intoYahooWeatherServiceImpl:
Listing 9. ZipCodeValidator incorporated into YahooWeatherServiceImpl

public String getWeatherHtml(String zip, boolean isCelsius) 
       throws WeatherException {

   if (!new ZipCodeValidator().isValid(zip)) {
      log.warn("Invalid zipcode: "+zip);
      throw new WeatherException("Zip-code must have 5 digits");
   }

 

Back to top

Calling native JavaScript with JSNI

Visual-effects libraries are becoming increasingly popular in Web application development, whether their effects are used to provide subtle user-interaction cues or just to add polish. I’d like to add some eye-candy to the Weather Reporter application. GWT doesn’t provide this type of functionality, but its JavaScript Native Interface (JSNI) offers a solution. JSNI lets you make JavaScript calls directly from GWT client Java code. This means that I can exploit effects from the Scriptaculous library (seeResources) or from the Yahoo! User Interface library, for example.

JSNI uses a cunning combination of the Java language’s native keyword and JavaScript embedded in a special comment block. It’s probably best explained by example, so Listing 10 shows a method that invokes a given Scriptaculous effect on anElement:
Listing 10. Invoking Scriptaculous effects with JSNI

/**
 * Publishes HTML to the weather display pane
 */
private void displayHtml(String html) {
   weatherHtml.setHTML(html);
   applyEffect(weatherHtml.getElement(), "Appear");
}

/**
 * Applies a Scriptaculous effect to an element
 * @param element The element to reveal
 */
private native void applyEffect(Element element, String effectName) /*-{

   // Trigger named Scriptaculous effect
   $wnd.Effect[effectName](element);
}-*/;

 

This is perfectly valid Java code because the compiler sees only private native void applyEffect(Element element, String effectName);. GWT parses the contents of the comment block and outputs the JavaScript verbatim. GWT provides the$wnd and $doc variables to refer to the window and document objects. In this case, I’m simply accessing the top-level Scriptaculous Effect object and using JavaScript’s square-bracket object-accessor syntax to invoke the named function specified by the caller. The Element type is a “magic” type provided by GWT that represents a Widget‘s underlying HTML DOM element in both Java and JavaScript code. Strings are one of the few other types that can be passed transparently between Java code and JavaScript via JSNI.

Now I have a weather report that fades in nicely when the data is returned from the server. The final touch is to re-enable the ZIP code TextBox when the effect has finished. Scriptaculous uses an asynchronous callback mechanism to notify listeners about the life cycle of effects. This is where things get a little more complex because I need to have JavaScript call back into my GWT client Java code. In JavaScript, you can invoke any function with an arbitrary number of arguments, so Java-style method overloading doesn’t exist. This means that JSNI needs to use an unwieldy syntax to refer to Java methods to disambiguate possible overloads. The GWT documentation states this syntax as:

[instance-expr.]@class-name::method-name(param-signature)(arguments)

 

The instance-expr. part is optional because static methods are invoked without the need for an object reference. Again, it’s easiest to see this illustrated by example, in Listing 11:
Listing 11. Calling back into Java code with JSNI

/**
 * Applies a Scriptaculous effect to an element
 * @param element The element to reveal
 */
private native void applyEffect(Element element, String effectName) /*-{

  // Keep reference to self for use inside closure
  var weather = this;

  // Trigger named Scriptaculous effect
  $wnd.Effect[effectName](element, { 
     afterFinish : function () {

     // Make call back to Weather object
     weather.@developerworks.gwt.weather.client.Weather::effectFinished()();
     } 
  });
}-*/;

/**
 * Callback triggered when a Scriptaculous effect finishes.
 * Re-enables the input textbox.
 */
private void effectFinished() {
  this.txBox.setEnabled(true);
  this.txBox.setFocus(true);
}

 

The applyEffect() method has been changed to pass an extra afterFinish argument to Scriptaculous. The value ofafterFinish is an anonymous function that is invoked when the effect is done. This is somewhat similar to the anonymous inner-class idiom used by GWT’s event handlers. The actual call back into Java code is made by specifying the instance of theWeather object to invoke the call on, then the fully qualified name of the Weather class, then the name of the function to invoke. The first empty pair of parentheses denotes that I want to call the method named effectFinished() that takes no arguments. The second set of parentheses calls the function.

The quirk here is that a local variable, weather, keeps a copy of the this reference. Because of the way JavaScript call semantics operate, the this variable inside the afterFinish function is actually a Scriptaculous object because Scriptaculous makes the function call. Making a copy of this outside the closure is a simple workaround.

Now that I’ve demonstrated some of JSNI’s capabilities, I should point out that a much better way of integrating Scriptaculous into GWT is by wrapping Scriptaculous effects functionality up as a custom GWT widget. This is exactly what Alexei Sokolov has done in the GWT Component Library (see Resources).

Now that I’m all done with the Weather Reporter application, I’ll examine some of the benefits and drawbacks of Web development with GWT.

Back to top

Why use GWT?

Contrary to what you might expect, GWT applications are remarkably un-Weblike. GWT essentially exploits the browser as a run-time environment for lightweight GUI applications, and the result is much closer to what you might develop with Morfik, OpenLaszlo, or even Flash, than to normal Web applications. Accordingly, GWT is best suited to Web applications that can exist as a rich Ajax GUI on a single page. It’s probably no coincidence that this characterizes some of Google’s recent beta releases, such as its Calendar and Spreadsheets applications. These are great applications, but you can’t solve all business scenarios using this approach. The majority of Web applications fit quite comfortably into the page-centric model, and Ajax lets richer interaction paradigms be employed where needed. GWT does not play nicely with traditional page-centric applications. Although it’s possible to combine GWT widgets with normal HTML form inputs, the state of a GWT widget is fenced off from the rest of the page. For example, there’s no straightforward way to submit the selected value from a GWT Tree widget as part of a regular form.

Licensing

GWT’s run-time libraries are licensed under the Apache License 2.0, and you can use GWT freely to create commercial applications. However, the GWT toolchain is provided in binary-only form, and modifications are not permitted. This includes the Java-to-JavaScript compiler. This means that any errors in your generated JavaScript are out of your control. A particular problem is GWT’s reliance on user-agent detection: Each release of a new browser requires an update to the GWT toolkit to provide support.

If you do decide to use GWT into a J2EE application environment, GWT’s design should make integration relatively straightforward. In this scenario, GWT services should be thought of as similar toActions in Struts — a thin middle layer that simply proxies Web requests into business-logic invocations on the back end. Because GWT services are just HTTP servlets, they can be integrated easily into Struts or SpringMVC, for example, and placed behind authentication filters.

GWT does have a couple of fairly significant flaws. First among them is its lack of provision for graceful degradation. Best practice in modern Web application development is to create pages that work without JavaScript, and then use it where available to embellish and add extra behavior. In GWT, if JavaScript isn’t available, you won’t get any UI at all. For certain classes of Web application, this is an immediate deal-breaker. Internationalization is also a major problem for GWT. Because GWT client Java classes run in the browser, they have no access to properties or resource bundles to grab localized strings at run time. A complex workaround is available that requires a subclass of each client-side class to be created for each locale (see Resources), but GWT’s engineers are working on a more viable solution.

The case for code generation

Probably the most contentious issue in GWT’s architecture is the switch to the Java language for client-side code. Some GWT proponents suggest that writing client-side code in the Java language is intrinsically preferable to writing JavaScript. This is not a view shared by all, and many JavaScript coders would be extremely reluctant to sacrifice their language’s flexibility and expressiveness for the sometimes onerous grind of Java development. One situation where the substitution of Java code for JavaScript might be appealing is in a team that lacks experienced Web developers. However, if that team is moving into Ajax development, it might be better served by hiring skilled JavaScript programmers rather than relying on Java coders to produce obfuscated JavaScript at arm’s length through a proprietary tool. Bugs are inevitably caused by leaks in the abstraction that GWT stretches over JavaScript, HTTP, and HTML, and inexperienced Web programmers have a hard time tracking them down. As developer and blogger Dimitri Glazkov puts it, “If you can’t handle JavaScript, you shouldn’t be writing code for Web applications. HTML, CSS, and JavaScript are the three prerequisites for this ride.” (see Resources).

Some people argue that Java coding is inherently less error-prone than JavaScript programming, thanks to static typing and compile-time checks. This is a fairly fallacious argument. It’s possible to write bad code in any language, and plenty of buggy Java applications are around to prove it. You’re also dependent on GWT’s code-generation being bug-free. However, offline syntax-checking and validation of client-side code can certainly be beneficial. It’s available for JavaScript in the form of Douglas Crockford’s JSLint (see Resources). GWT has the upper hand in terms of unit testing, though, providing JUnit integration for client-side code. Unit-testing support is an area where JavaScript is still sorely lacking.

In developing the Weather Reporter application, the most compelling case I found for client-side Java code was the ability to share the same validation class between both tiers. This obviously saves development effort. The same goes for any classes transferred over RPC; you only need to code them once and you can use them both client and server code. Unfortunately, the abstraction is leaky: in my ZIP code validator, for example, I would have liked to use regular expressions to perform the check. However, GWT doesn’t implement the String.match() method. Even if it did, regular expressions in GWT have syntactical differences when deployed as client and server code. This is because of GWT’s reliance on the host environment’s underlying regexp mechanism and is an example of the trouble that imperfect abstractions can land you in.

One big win that GWT scores is its RPC mechanism and built-in serialization of objects between Java code and JavaScript. This removes a lot of the heavy lifting you see in the average Ajax application. It’s not without precedent, though. If you want this functionality without the rest of GWT, then Direct Web Remoting (DWR), which offers RPC with object marshalling to and from Java code to JavaScript, is well worth considering (see Resources).

GWT also does a good job of abstracting away some of the low-level aspects of Ajax application development, such as cross-browser incompatibilities, the DOM event model, and making Ajax calls. But modern JavaScript toolkits such as the Yahoo! UI Library, Dojo, and MochiKit all provide a similar level of abstraction without needing to resort to code generation. Moreover, all of these toolkits are open source, so you can customize them to suit your needs or fix bugs that arise. This isn’t possible with the black box of GWT (see the Licensing sidebar).

Back to top

Conclusion

GWT is a comprehensive framework that provides a great deal of useful functionality. However, GWT is something of an all-or-nothing approach, targeted at a relatively small niche in Web application development market. I hope this brief tour has given you a feel for GWT’s capabilities and its limitations. Although it certainly won’t suit everyone’s needs, GWT remains a major engineering achievement and is worthy of serious consideration when you design your next Ajax application. GWT has more breadth and depth than I’ve been able to explore here, so do read Google’s documentation for more or join the discussion on the GWT developer forum (see Resources).

 

Back to top

Download

Description Name Size Download method
GWT Weather Reporter application j-ajax4-gwt-weather.zip 2.1KB HTTP

Information about download methods

 

Resources

Learn

Get products and technologies

Discuss

  • GWT Developer Forum: Discussion group frequented by the developers of GWT, also serving as an ad-hoc source of GWT documentation.
  • developerWorks blogs: Get involved in the developerWorks community.

About the author

Philip McCarthy is a software development consultant specializing in Java and Web technologies. He has recently been involved in digital media and telecoms projects at Hewlett Packard Labs and Orange and is currently working on financial software in the City of London.

original:http://www.ibm.com/developerworks/java/library/j-ajax4/index.html

[repost ] Ajax for Java developers: Ajax with Direct Web Remoting

Summary: Exciting as it is, adding Ajax functionality to your applications can mean a lot of hard work. In this third article in the Ajax for Java™ developers series, Philip McCarthy shows you how to use Direct Web Remoting (DWR) to expose JavaBeans methods directly to your JavaScript code and automate the heavy-lifting of Ajax.

View more content in this series

Understanding the fundamentals of Ajax programming is essential, but if you’re building complex Ajax UIs, it’s also important to be able to work at a higher level of abstraction. In this third article in the Ajax for Java developers series, I build on last month’s introduction to data serialization techniques for Ajax, introducing a technique that will let you avoid the nitty-gritty details of serializing Java objects.

In the previous article, I showed you how to use JavaScript Object Notation (JSON) to serialize data in a format easily converted into JavaScript objects on the client. With this setup, you can invoke remote service calls using JavaScript code and receive JavaScript object graphs in response, not unlike making a remote procedure call. This time, you learn how to take things one step further, using a framework that formalizes your ability to make remote procedure calls on server-side Java objects from JavaScript client code.

DWR is an open source, Apache licensed solution consisting of server-side Java libraries, a DWR servlet, and JavaScript libraries. While DWR is not the only Ajax-RPC toolkit available for the Java platform, it is one of the most mature, and it offers a great deal of useful functionality. See Resources to download DWR before proceeding with the examples.

What is DWR?

In the simplest terms, DWR is an engine that exposes methods of server-side Java objects to JavaScript code. Effectively, with DWR, you can eliminate all of the machinery of the Ajax request-response cycle from your application code. This means your client-side code never has to deal with an XMLHttpRequest object directly, or with the server’s response. You don’t need to write object serialization code or use third-party tools to turn your objects into XML. You don’t even need to write servlet code to mediate Ajax requests into calls on your Java domain objects.

DWR is deployed as a servlet within your Web application. Viewed as a black box, this servlet performs two major roles: First, for each exposed class, DWR dynamically generates JavaScript to include in your Web page. The generated JavaScript contains stub functions that represent the corresponding methods on the Java class and also performs XMLHttpRequests behind the scenes. These requests are sent to the DWR servlet, which, in its second role, translates the request into a method call on a server-side Java object and sends the method’s return value back to the client side in its servlet response, encoded into JavaScript. DWR also provides JavaScript utility functions that help perform common UI tasks.

Back to top

About the example

Before explaining DWR in more detail, I’ll introduce a simple example scenario. As in the previous articles, I’ll use a minimal model based on an online store, this time consisting of a basic product representation, a user’s shopping cart that can contain product items, and a data access object (DAO) to look up product details from a data store. The Item class is the same one used in the previous article, but it no longer implements any manual serialization methods. Figure 1 depicts this simple setup:
Figure 1. Class diagram depicting the Cart, CatalogDAO, and Item classes

I’ll demonstrate two very simple use cases within this scenario. First, the user can perform a text search on the catalog and see matching items. Second, the user can add items to the shopping cart and see the total cost of items in the cart.

Back to top

Implementing the catalog

The starting point of a DWR application is writing your server-side object model. In this case, I start by writing a DAO to provide search capabilities on the product catalog datastore. CatalogDAO.java is a simple, stateless class with a zero-argument constructor. Listing 1 shows the signatures of the Java methods that I want to expose to Ajax clients:
Listing 1. The CatalogDAO methods to expose via DWR

/**
 * Returns a list of items in the catalog that have 
 *  names or descriptions matching the search expression
 * @param expression Text to search for in item names 
 *  and descriptions 
 * @return list of all matching items
 */
public List<Item> findItems(String expression);

/**
 * Returns the Item corresponding to a given Item ID
 * @param id The ID code of the item
 * @return the matching Item
 */
public Item getItem(String id);

 

Next, I need to configure DWR, telling it that Ajax clients should be able to construct a CatalogDAO and call these methods. I do this with the dwr.xml config file shown in Listing 2:
Listing 2. Config to expose CatalogDAO methods

<!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="catalog">
      <param name="class" 
        value="developerworks.ajax.store.CatalogDAO"/>
      <include method="getItem"/> 
      <include method="findItems"/> 
    </create> 
    <convert converter="bean" 
      match="developerworks.ajax.store.Item">
      <param name="include" 
        value="id,name,description,formattedPrice"/>
    </convert>
  </allow>
</dwr>

 

The root element of the dwr.xml document is dwr. Inside this element is the allow element, which specifies the classes that DWR will remote. The two child elements of allow are create and convert.

The create element

The create element tells DWR that a server-side class should be exposed to Ajax requests and defines how DWR should obtain an instance of that class to remote. The creator attribute here is set to the value new, meaning that DWR should call the class’s default constructor to obtain an instance. Other possibilities are to create an instance through a fragment of script using the Bean Scripting Framework (BSF), or to obtain an instance via integration with the IOC container, Spring. By default, when an Ajax request to DWR invokes a creator, the instantiated object is placed in page scope and therefore is no longer available after the request completes. In the case of the stateless CatalogDAO, this is fine.

The javascript attribute of create specifies the name by which the object will be accessible from JavaScript code. Nested within the create element, a param element specifies the Java class that the creator will create. Finally, include elements specify the names of the methods that should be exposed. Explicitly stating the methods to expose is good practice to avoid accidentally allowing access to potentially harmful functionality — if this element is omitted, all of the class’s methods will be exposed to remote calls. Alternately, you can use exclude elements to specify only those methods you wish to prevent access to.

The convert element

While creators are concerned with exposing classes and their methods for Web remoting, convertors are concerned with the parameters and return types of those methods. The role of the convert element is to tell DWR how to convert datatypes between their server-side Java object representation and serialized JavaScript representation, and vice versa.

DWR automatically mediates simple data types between Java and JavaScript representations. These types include Java primitives, along with their respective class representations, as well as Strings and Dates, arrays, and collection types. DWR can also convert JavaBeans into JavaScript representations, but for security reasons, doing so requires explicit configuration.

The convert element in Listing 2 tells DWR to use its reflection-based bean convertor for the Items returned by the exposed methods of CatalogDAO and specifies which of the Item‘s members should be included in the serialization. The members are specified using the JavaBean naming convention, so DWR will call the corresponding get methods. In this case, I’m omitting the numerical price field and instead including the formattedPrice field, which is currency-formatted ready for display.

At this point, I’m ready to deploy my dwr.xml to my Web application’s WEB-INF directory, where the DWR servlet will pick it up. Before proceeding, however, it’s a good idea to ensure that everything is working as expected.

Back to top

Testing the deployment

If the DWRServlet‘s web.xml definition sets the init-param debug to true, then DWR’s extremely helpful test mode is enabled. Navigating to /{your-web-app}/dwr/ brings up a list of your classes that DWR has been configured to remote. Clicking through takes you to the status screen for a given class. The DWR test page for CatalogDAO is shown in Figure 2. As well as providing a script tag to paste into your Web pages, pointing to DWR’s generated JavaScript for the class, this screen also provides a list of the class’s methods. The list includes methods inherited from the class’s supertypes, but only those methods that I explicitly specified for remoting in dwr.xml are marked as accessible.
Figure 2. DWR test page for CatalogDAO
The diagnostic and test page generated by DWR for CatalogDAO

It’s possible to enter parameter values into the textboxes next to the accessible methods and hit the Execute button to invoke them. The server’s response will be displayed using JSON notation in an alert box, unless it is a simple value, in which case it will be displayed inline alongside the method. These test pages are very useful. Not only do they allow you to easily check which classes and methods are exposed for remoting, you can also test that each method is behaving as expected.

Once you’re satisfied that your remoted methods are working correctly, you can use DWR’s generated JavaScript stubs to call on your server-side objects from client-side code.

Back to top

Calling a remoted object

The mapping between remoted Java object methods and their corresponding JavaScript stub functions is simple. The general form is JavaScriptName.methodName(methodParams ..., callBack), where JavaScriptName is whatever name was specified as the creator‘s javascript attribute, methodParams represents the Java method’s n parameters, and callback is a JavaScript function that will be called with the Java method’s return value. If you’re familiar with Ajax, you’ll recognize the callback mechanism as the usual approach to XMLHttpRequest‘s asynchrony.

In the example scenario, I use the JavaScript functions in Listing 3 to perform a search and update the UI with the search results. This listing also uses convenience functions from DWR’s util.js. Of particular note is the JavaScript function named $(), which can be thought of as a souped-up version of document.getElementById(). It’s certainly easier to type. If you’ve used the prototype JavaScript library, you’ll be familiar with this function.
Listing 3. Calling the remoted findItems() method from the client

/*
 * Handles submission of the search form
 */
function searchFormSubmitHandler() {

  // Obtain the search expression from the search field
  var searchexp = $("searchbox").value;

  // Call remoted DAO method, and specify callback function
  catalog.findItems(searchexp, displayItems);

  // Return false to suppress form submission
  return false;
}

/*
 * Displays a list of catalog items
 */
function displayItems(items) {

  // Remove the currently displayed search results
  DWRUtil.removeAllRows("items");

  if (items.length == 0) {
    alert("No matching products found");
    $("catalog").style.visibility = "hidden";
  } else {

    DWRUtil.addRows("items",items,cellFunctions);
    $("catalog").style.visibility = "visible";
  }
}

 

In the above searchFormSubmitHandler() function, the code of interest is of course catalog.findItems(searchexp, displayItems);. This single line of code is all that is needed to send an XMLHttpRequest over the network to the DWR servlet and call the displayItems() function with the remoted object’s response.

The displayItems() callback itself is invoked with an array of Item representations. This array is passed to theDWRUtil.addRows() convenience function, along with the ID of a table to populate and an array of functions. There are as many functions in this array as there are cells in each table row. Each function is called in turn with an Item from the array and should return the content with which to populate the corresponding cell.

In this case, I want each row in the table of items to display the item’s name, description, and price, as well as an Add to Cartbutton for the item in the last column. Listing 4 shows the cell functions array that accomplishes this:
Listing 4. Cell functions array to populate the items table

/*
 * Array of functions to populate a row of the items table
 * using DWRUtil's addRows function
 */
var cellFunctions = [
  function(item) { return item.name; },
  function(item) { return item.description; },
  function(item) { return item.formattedPrice; },
  function(item) {
    var btn = document.createElement("button");
    btn.innerHTML = "Add to cart";
    btn.itemId = item.id;
    btn.onclick = addToCartButtonHandler;
    return btn;
  }
];

 

The first three of these functions simply return the content of the fields included in Item‘s convertor in dwr.xml. The last function creates a button, attaches the Item‘s ID to it, and specifies that a function named addToCartButtonHandler should be called when the button is clicked. This function is the entry point for the second use case: adding an Item to the shopping cart.

Back to top

Implementing the shopping cart

Security in DWR

DWR has been designed with security in mind. Using dwr.xml to whitelist only those classes and methods you wish to remote avoids accidental exposure of functionality that could be maliciously exploited. In addition to this, using the debug Test Mode, it is easy to audit all of the classes and methods that are exposed to the Web.

DWR also supports role-based security. You can specify the J2EE role that a user must be in to access a particular bean, via its creator configuration. By deploying multiple URL-secured instances of theDWRServlet, each with its own dwr.xml config file, you can also provide different sets of users with different remoted functionality.

The Java representation of the user’s shopping cart is based on aMap. When an Item is added to the cart, the Item itself is inserted into the Map as the key. The corresponding value in the Map is anInteger representing the quantity of the given Item in the cart. Thus the Cart.java has a field, contents, declared asMap<Item,Integer>.

Using complex types as hash keys presents a problem to DWR — in JavaScript, array keys must be literals. As a result, the contents Mapcannot be converted by DWR as it is. However, for the purposes of the shopping cart UI, all the user needs to see is the name and quantity of each item in the cart. So I can add a method namedgetSimpleContents() to Cart, which takes the contents Map and builds a simplified Map<String,Integer> from it, representing only the name and quantity of each Item. This string-keyed map representation can simply be converted into JavaScript by DWR’s built-in convertors.

The other field of Cart that the client is interested in is thetotalPrice, representing the sum total of everything in the shopping cart. As with Item, I’ve provided a synthetic member named formattedTotalPrice that is a pre-formatted Stringrepresentation of the numerical total.

Converting the Cart

Instead of having the client code make two calls on Cart, one to obtain the contents and one for the total price, I want to send all of this data to the client at once. To accomplish this I’ve added the strange-looking method, shown in Listing 5:
Listing 5. Cart.getCart() method

/**
 * Returns the cart itself - for DWR
 * @return the cart
 */ 
public Cart getCart() {
  return this;
}

 

While this method would be completely redundant in normal Java code (because you already have a reference to the Cart if you’re calling the method), it allows a DWR client to have Cart serialize itself to JavaScript.

Aside from getCart(), the other method that needs to be remoted is addItemToCart(). This method takes a Stringrepresentation of a catalog Item’s ID, adds that item to the Cart, and updates the price total. The method also returns the Cart, so that the client code can update the Cart contents and receive its new state in a single operation.

Listing 6 is the extended dwr.xml config file that includes the extra config info for remoting the Cart class:
Listing 6. Modified dwr.xml incorporating the Cart class

<!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="catalog">
      <param name="class" 
        value="developerworks.ajax.store.CatalogDAO"/>
      <include method="getItem"/>
      <include method="findItems"/>
    </create>
    <convert converter="bean" 
      match="developerworks.ajax.store.Item">
      <param name="include" 
        value="id,name,description,formattedPrice"/>
    </convert>
    <create creator="new" scope="session" javascript="Cart">
      <param name="class" 
        value="developerworks.ajax.store.Cart"/>
      <include method="addItemToCart"/>
      <include method="getCart"/>
    </create>
    <convert converter="bean" 
      match="developerworks.ajax.store.Cart">
      <param name="include" 
        value="simpleContents,formattedTotalPrice"/>
    </convert>
  </allow>
</dwr>

 

In this version of dwr.xml, I’ve added both a creator and a convertor for Cart. The create element specifies that theaddItemToCart() and getCart() methods should be remoted and, importantly, that the created Cart instance should be placed in the user’s session. As a result, the content of the cart will persist between user requests.

The convert element for Cart is necessary because the remoted Cart methods return the Cart itself. Here I’ve specified that the members of Cart that should be present in its serialized JavaScript form are the map simpleContents and the StringformattedTotalPrice.

If you find this slightly confusing, remember that the create element specifies the server-side methods on Cart that can be called by a DWR client, and the convert element specifies the members to include in the JavaScript serialization of Cart.

Now I can implement the client-side code that calls my remoted Cart methods.

Back to top

Calling remoted Cart methods

First of all, when the store Web page first loads, I want to check the state of the Cart stored in session, if there is one. This is necessary because the user may have added items to the Cart and then refreshed the page or navigated away and then back again. In these circumstances, the reloaded page needs to sync itself with the Cart data in session. I can accomplish this with a call performed in the page’s onload function, like so: Cart.getCart(displayCart). Note that displayCart() is a callback function invoked with the Cart response data from the server.

If a Cart is in session already, then the creator will retrieve it and call its getCart() method. If no Cart is in session, then thecreator will instantiate a new one, place it in session, and call the getCart() method.

Listing 7 shows the implementation of the addToCartButtonHandler() function that is called when an item’s Add to Cartbutton is clicked:
Listing 7. addToCartButtonHandler() implementation

/*
 * Handles a click on an Item's "Add to Cart" button
 */
function addToCartButtonHandler() {

  // 'this' is the button that was clicked.
  // Obtain the item ID that was set on it, and
  // add to the cart.
  Cart.addItemToCart(this.itemId,displayCart);
}

 

With DWR taking care of all the communication, the add-to-cart behavior on the client is literally a one-line function. Listing 8 shows the final piece of this jigsaw puzzle — the implementation of the displayCart() callback, which updates the UI with the state of the Cart:
Listing 8. displayCart() implementation

/*
 * Displays the contents of the user's shopping cart
 */
function displayCart(cart) {

  // Clear existing content of cart UI
  var contentsUL = $("contents");
  contentsUL.innerHTML="";

  // Loop over cart items
  for (var item in cart.simpleContents) {

    // Add a list element with the name and quantity of item
    var li = document.createElement("li");
    li.appendChild(document.createTextNode(
                    cart.simpleContents[item] + " x " + item
                  ));
    contentsUL.appendChild(li);
  }

  // Update cart total
  var totalSpan = $("totalprice");
  totalSpan.innerHTML = cart.formattedTotalPrice;
}

 

It’s important to remember here that simpleContents is a JavaScript array mapping Strings to numbers. Each string is the name of an item, and the corresponding number in the associative array is the quantity of that item in the cart. So the expressioncart.simpleContents[item] + " x " + item evaluates to, for example, “2 x Oolong 128MB CF Card“.

The DWR Store application

Figure 3 shows my DWR-based Ajax application in action, displaying items retrieved by a search with the user’s shopping cart on the right:
Figure 3. The DWR-based Ajax store application in action
Screenshot of example scenario, with search results and shopping cart

Back to top

Pros and cons of DWR

Call batching

In DWR, several remote calls can be sent to the server with a single HTTP request. CallingDWREngine.beginBatch() tells DWR to not dispatch subsequent remoted calls straight away, but instead to combine them into a single batched request. A call toDWREngine.endBatch() sends the batched request to the server. The remote calls are executed in order on the server-side, and then each JavaScript callback is invoked.

Batching can help to reduce latency in two ways: first, you avoid the overhead of creating an XMLHttpRequestobject and establishing the associated HTTP connection for each call. Second, in a production environment, the Web server won’t have to deal with so many concurrent HTTP requests, improving response times.

You’ve seen how easy it is to implement a Java-backed Ajax application using DWR. Although the example scenario is a simple one and I’ve taken a fairly minimal approach to implementing the use cases, you shouldn’t underestimate the amount of work the DWR engine can save you over a homegrown Ajax approach. Whereas in previous articles I walked through all the steps of manually setting up an Ajax request and response and converting a Java object graph into a JSON representation, here DWR has done all of that work for me. I wrote fewer than 50 lines of JavaScript to implement the client, and on the server-side, all I had to do was augment my regular JavaBeans with a couple of extra methods.

Of course, every technology has its drawbacks. As with any RPC mechanism, in DWR it can be easy to forget that each call you make on remoted objects is much more expensive than a local function call. DWR does a great job of hiding the Ajax machinery, but it’s important to remember that the network is not transparent — there is latency involved in making DWR calls, and your application should be architected so that remoted methods are coarse-grained. It is for this purpose that addItemToCart() returns the Cart itself. Although it would have been more natural to makeaddItemToCart() a void method, each DWR call to it would then have had to be followed with a call to getCart() to retrieve the modified Cart state.

DWR does have its own solution to the latency issue in call batching (see the sidebar Call batching). If you’re unable to provide a suitably coarse-grained Ajax interface for your application, then use call batching wherever possible to combine multiple remote calls into a single HTTP request.

Separation of concerns

By its very nature, DWR creates a tight coupling between client-side and server-side code, with a number of implications. First, changes to the API of remoted methods need to be reflected in the JavaScript that calls on the DWR stubs. Second (and more significantly), this coupling causes client-side concerns to leak into server-side code. For instance, because not all Java types can be converted into JavaScript, it is sometimes necessary to add extra methods to Java objects so that they can be remoted more easily. In the example scenario, I resolved this by adding a getSimpleContents() method to Cart. I also added thegetCart() method, which is useful in a DWR scenario but otherwise completely redundant. Given the need for a coarse-grained API on remoted objects and the problem of converting certain Java types to JavaScript, you can see how remoted JavaBeans can become polluted with methods that are useful only to an Ajax client.

To get around this, you might use wrapper classes to add extra DWR-specific methods to your plain-old JavaBeans. This would mean that Java clients of the JavaBean classes wouldn’t see the extra fluff associated with remoting, and it would also allow you to give more friendly names to remoted methods — getPrice() instead of getFormattedPrice(), for instance. Figure 4 shows a RemoteCart class that wraps Cart to add the extra DWR functionality:
Figure 4. RemoteCart wraps Cart for remoting functionality
Class diagram of RemoteCart wrapper class

Finally, you need to remember that DWR Ajax calls are asynchronous and should not be expected to return in the order they were dispatched. I ignored this small hurdle in example code, but in the first article of this series, I demonstrated how to timestamp responses as a simple safeguard against data arriving out-of-order.

Back to top

In conclusion

As you’ve seen, DWR has a lot to offer — it allows you to create an Ajax interface to your server-side domain objects quickly and simply, without needing to write any servlet code, object serialization code, or client-side XMLHttpRequest code. It’s extremely simple to deploy to your Web application using DWR, and DWR’s security features can be integrated with a J2EE role-based authentication system. DWR isn’t applicable to every application architecture, however, and it does require you to give some thought to the design of your domain objects’ APIs.

If you want to learn more about the pros and cons of Ajax with DWR, the best thing would be to download it for yourself and start experimenting. While DWR has many features that I haven’t covered, the article source code is a good starting point for taking DWR for a spin. See Resources to learn more about Ajax, DWR, and related technologies.

One of the most important points to take away from this series is that for Ajax applications, there is no one-size-fits-all solution. Ajax is a rapidly developing field with new technologies and techniques emerging all the time. In the three articles of this series, I’ve focused on getting you started with leveraging Java technologies in the Web tier of an Ajax application — whether you choose an XMLHttpRequest-based approach with an object serialization framework or the higher level abstraction of DWR. Look out for further articles exploring Ajax for Java developers in the coming months.

 

Back to top

Download

Description Name Size Download method
DWR source code j-ajax3dwr.zip 301 KB HTTP

Information about download methods

 

Resources

Learn

Get products and technologies

Discuss

About the author

Philip McCarthy is software development consultant specializing in Java and Web technologies. He is currently working on Hewlett Packard’s Digital Media Platform project at HP Labs, Bristol. In recent years Phil has developed several rich Web clients employing asynchronous server communication and DOM scripting. He is glad we now have a name for them. You can get in touch with Phil at philmccarthy@gmail.com.

original:http://www.ibm.com/developerworks/java/library/j-ajax3/index.html

[repost ] Ajax for Java developers: Write scalable Comet applications with Jetty and Direct Web Remoting (Create event-driven Web applications using Continuations and Reverse Ajax)

Summary: Ajax applications driven by asynchronous server-side events can be tricky to implement and difficult to scale. Returning to his popular series, Philip McCarthy shows an effective approach: The Comet pattern allows you to push data to clients, and Jetty 6’s Continuations API lets your Comet application scale to a large number of clients. You can conveniently take advantage of both Comet and Continuations with the Reverse Ajax technology in Direct Web Remoting 2.

View more content in this series

With Ajax firmly established as a widespread Web application-development technique, several common Ajax usage patterns have emerged. For instance, Ajax is often used in response to user input to modify parts of a page with new data fetched from the server. Sometimes, though, a Web application’s user interface needs to update in response to server-side events that occur asynchronously, without user action — for instance, to show new messages arriving in an Ajax chat application or to display changes from another user in a collaborative text editor. Because HTTP connections between a Web browser and a server can be established only by the browser, the server can’t “push” changes to the browser as they occur.

Ajax applications can use two fundamental approaches to work around this problem: either the browser can poll the server for updates every few seconds, or the server can hold open a connection from the browser and pass data as it becomes available. This long-lived connection technique has become known as Comet (see Resources). This article shows how you can use the Jetty servlet engine and DWR together to implement a Comet Web application simply and efficiently.

Why Comet?

The main drawback of the polling approach is the amount of traffic generated as it scales to many clients. Each client must regularly hit the server to check for updates, which places a burden on the server’s resources. The worst situation is an application involving infrequent updates, such as an Ajax mail Inbox. In this case, the vast majority of client polls would be redundant, with the server simply answering “no data yet.” The server load can be alleviated by increasing the polling interval, but this has the undesirable consequence of introducing a lag between a server event and the client’s awareness of it. Of course, a reasonable balance can be found for many applications, and polling works acceptably well.

Nevertheless, one of the appeals of the Comet strategy is its perceived efficiency. Clients don’t produce the noisy traffic characteristic of polling, and as soon as events occur, they can be published to the client. But holding open long-lived connections consumes server resources too. While a servlet holds a persistent request in a waiting state, that servlet is monopolizing a thread. This limits Comet’s scalability with a traditional servlet engine because the number of clients can quickly overwhelm the number of threads that the server stack can handle efficiently.

Back to top

How Jetty 6 differs

Jetty 6 is designed to scale to large numbers of simultaneous connections, exploiting the Java™ language’s nonblocking I/O (java.nio) libraries and using an optimised output-buffer architecture (see Resources). Jetty also has a trick up its sleeve for dealing with long-lived connections: a feature known as Continuations. I’ll demonstrate Continuations with a simple servlet that receives a request, waits for two seconds, and then sends a response. Next, I’ll show what happens when the server has more clients than it has threads to handle them. Finally, I’ll reimplement the servlet using Continuations, and you’ll see the difference that they make.

To make it easier to follow what’s happening in the following examples, I’ll restrict the Jetty servlet engine to a single request-handling thread. Listing 1 shows the relevant configuration in jetty.xml. I actually need to allow a total of three threads in theThreadPool: the Jetty server itself uses one, and another runs an HTTP connector, listening for incoming requests. This leaves one thread to execute servlet code.
Listing 1. Jetty configuration for a single servlet thread

                
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN"
  "http://jetty.mortbay.org/configure.dtd">
<Configure id="Server">
    <Set name="ThreadPool">
      <New>
        <Set name="minThreads">3</Set>
        <Set name="lowThreads">0</Set>
        <Set name="maxThreads">3</Set>
      </New>
    </Set>
</Configure>

 

Next, to simulate waiting for an asynchronous event, Listing 2 shows the service() method for BlockingServlet, which simply uses a Thread.sleep() call to pause for 2,000 milliseconds before completing. It also outputs the system time at the beginning and end of execution. To help disambiguate output from different requests, it also logs a request parameter used as an identifier.
Listing 2. BlockingServlet

                
public class BlockingServlet extends HttpServlet {

  public void service(HttpServletRequest req, HttpServletResponse res)
                                              throws java.io.IOException {

    String reqId = req.getParameter("id");

    res.setContentType("text/plain");
    res.getWriter().println("Request: "+reqId+"\tstart:\t" + new Date());
    res.getWriter().flush();

    try {
      Thread.sleep(2000);
    } catch (Exception e) {}

    res.getWriter().println("Request: "+reqId+"\tend:\t" + new Date());
  }
}

 

Now you can observe the servlet’s behaviour in response to several simultaneous requests. Listing 3 shows the console output of five parallel requests using lynx. The command line simply launches five lynx processes, appending an identifying ordinal to the request URL.
Listing 3. Output from several concurrent requests to BlockingServlet

                
$ for i in 'seq 1 5'  ; do lynx -dump localhost:8080/blocking?id=$i &  done
Request: 1      start:  Sun Jul 01 12:32:29 BST 2007
Request: 1      end:    Sun Jul 01 12:32:31 BST 2007

Request: 2      start:  Sun Jul 01 12:32:31 BST 2007
Request: 2      end:    Sun Jul 01 12:32:33 BST 2007

Request: 3      start:  Sun Jul 01 12:32:33 BST 2007
Request: 3      end:    Sun Jul 01 12:32:35 BST 2007

Request: 4      start:  Sun Jul 01 12:32:35 BST 2007
Request: 4      end:    Sun Jul 01 12:32:37 BST 2007

Request: 5      start:  Sun Jul 01 12:32:37 BST 2007
Request: 5      end:    Sun Jul 01 12:32:39 BST 2007

 

The output in Listing 3 is no surprise. Because only one thread is available for Jetty to execute the servlet’s service() method, Jetty queues each request and services it serially. The time stamps show that immediately after a response is dispatched for one request (an end message), the servlet begins working on the next request (the subsequent start message). So even though all five requests were sent simultaneously, one request must wait eight seconds before the servlet can handle it.

Remember that no useful work is being performed while the servlet blocks. This code simulates a situation where the request is waiting for an event to arrive asynchronously from another part of the application. The server is neither CPU- nor I/O-bound here: requests are queued only as a result of thread-pool exhaustion.

Now, check out how the Continuations feature in Jetty 6 can be helpful in this kind of situation. Listing 4 shows theBlockingServlet from Listing 2 rewritten using the Continuations API. I’ll explain the code a little later.
Listing 4. ContinuationServlet

                
public class ContinuationServlet extends HttpServlet {

  public void service(HttpServletRequest req, HttpServletResponse res)
                                              throws java.io.IOException {

    String reqId = req.getParameter("id");

    Continuation cc = ContinuationSupport.getContinuation(req,null);

    res.setContentType("text/plain");
    res.getWriter().println("Request: "+reqId+"\tstart:\t"+new Date());
    res.getWriter().flush();

    cc.suspend(2000);

    res.getWriter().println("Request: "+reqId+"\tend:\t"+new Date());
  }
}

 

Listing 5 shows the output from five simultaneous requests to ContinuationServlet; compare with Listing 3.
Listing 5. Output from several concurrent requests to ContinuationServlet

                
$ for i in 'seq 1 5'  ; do lynx -dump localhost:8080/continuation?id=$i &  done

Request: 1      start:  Sun Jul 01 13:37:37 BST 2007
Request: 1      start:  Sun Jul 01 13:37:39 BST 2007
Request: 1      end:    Sun Jul 01 13:37:39 BST 2007

Request: 3      start:  Sun Jul 01 13:37:37 BST 2007
Request: 3      start:  Sun Jul 01 13:37:39 BST 2007
Request: 3      end:    Sun Jul 01 13:37:39 BST 2007

Request: 2      start:  Sun Jul 01 13:37:37 BST 2007
Request: 2      start:  Sun Jul 01 13:37:39 BST 2007
Request: 2      end:    Sun Jul 01 13:37:39 BST 2007

Request: 5      start:  Sun Jul 01 13:37:37 BST 2007
Request: 5      start:  Sun Jul 01 13:37:39 BST 2007
Request: 5      end:    Sun Jul 01 13:37:39 BST 2007

Request: 4      start:  Sun Jul 01 13:37:37 BST 2007
Request: 4      start:  Sun Jul 01 13:37:39 BST 2007
Request: 4      end:    Sun Jul 01 13:37:39 BST 2007

 

There are two important things to note in Listing 5. First, each start message appears twice; don’t worry about this for the moment. Second, and more important, the requests are now handled concurrently, without queueing. Note that the time stamps of all the start and end messages are the same, at least at this resolution. Consequently, no request takes longer than two seconds to complete, even though only a single servlet thread is running.

Back to top

Inside Jetty’s Continuations mechanism

An understanding of how Jetty’s Continuations mechanism is implemented will explain the effects you see in Listing 5. To use Continuations, Jetty must be configured to handle requests with its SelectChannelConnector. This connector is built on thejava.nio APIs, allowing it to hold connections open without consuming a thread for each. When theSelectChannelConnector is used, ContinuationSupport.getContinuation() provides an instance ofSelectChannelConnector.RetryContinuation. (However, you should code against the Continuation interface only; seePortability and the Continuations API.) When suspend() is called on RetryContinuation, it throws a special runtime exception — RetryRequest — which propagates out of the servlet and back through the filter chain and is caught inSelectChannelConnector. But instead of sending any response to the client as a result of the exception, the request is held in a queue of pending Continuations, and the HTTP connection is kept open. At this point, the thread that was used to service the request is returned to the ThreadPool, where it can be used to service another request.

Portability and the Continuations API

I mentioned you should use Jetty’sSelectChannelConnector to enable Continuations functionality. However, the Continuations API is still valid with a traditional SocketConnector, in which case Jetty falls back to a different Continuationimplementation that uses wait()/notify()behaviour. Your code will still compile and run, but without the benefits of nonblocking Continuations. If you want to keep the option of using a non-Jetty server, you could consider writing your own Continuationwrapper that uses reflection to check for the availability of Jetty’s Continuations library at run time. DWR uses this strategy.

The suspended request remains in the pending Continuations queue until either the specified timeout expires, or the resume()method is called on its Continuation (more on this later). When either of these conditions occurs, the request is resubmitted to the servlet (via the filter chain). In effect, the entire request is “replayed” up until the point where suspend() was first called. When execution reaches the suspend() call the second time, the RetryRequestexception is not thrown, and execution continues as normal.

The output in Listing 5 should make sense now. As each request, in turn, enters the servlet’s service() method, the start message is sent in response, and then the Continuation‘s suspend() method causes execution to leave the servlet, freeing up the thread to begin servicing the next request. All five requests quickly run through the first part of the service() method and enter the suspended state, and all of the start messages are output within milliseconds. Two seconds later, as the suspend() timeouts expire, the first request is retrieved from the pending queue and resubmitted to theContinuationServlet. The start message is output a second time, the second call to suspend() returns immediately, and the end message is sent in response. The servlet code then executes again for the next queued request, and so on.

So, in both the BlockingServlet and ContinuationServlet cases, requests are queued for access to the single servlet thread. However, while the two-second pause in BlockingServlet occurs inside the servlet’s thread of execution,ContinuationServlet‘s pause occurs outside of the servlet in SelectChannelConnector. The overall throughput ofContinuationServlet is higher because the servlet thread isn’t tied up most of the time in a sleep() call.

Back to top

Making Continuations useful

Now that you’ve seen that Continuations allow servlet requests to be suspended without thread consumption, I need to explain a little bit more of the Continuations API to show you how to use Continuations for practical purposes.

resume() method forms a pair with suspend(). You can think of them of as the Continuations equivalent of the standardObject wait()/notify() mechanism. That is, suspend() puts a Continuation (and therefore the execution of the current method) on hold until either its timeout expires or another thread calls resume(). The suspend()/resume() pair is key to implementing a real Comet-style service using Continuations. The basic pattern is to obtain the Continuation from the current request, call suspend(), and wait until your asynchronous event arrives. Then call resume() and generate a response.

However, unlike the true language-level continuations in languages such as Scheme, or indeed the Java language’swait()/notify() paradigm, calling resume() on a Jetty Continuation doesn’t mean that code execution picks up exactly where it left off. As you’ve seen, what actually happens is that the request associated with the Continuation is replayed. This results in two problems: undesirable reexecution of code as in ContinuationServlet in Listing 4, and loss of state: anything in scope when the call is made to suspend() is lost.

The solution to the first of these issues is the isPending() method. If the return value of isPending() is true, this means thatsuspend() has been called previously, and execution of the retried request has not yet reached suspend() for the second time. In other words, making code prior to your suspend() call conditional on isPending() ensures that it executes only once per request. It’s best to design your application code before the suspend() call to be idempotent, so that calling it twice won’t matter anyway, but where that isn’t possible you can use isPending()Continuation also offers a simple mechanism for preserving state: the putObject(Object) and getObject() methods. Use these to hold a context object with any state you need to preserve when the Continuation is suspended. You can also use this mechanism as a way of passing event data between threads, as you’ll see later on.

Back to top

Writing a Continuations-based application

As a vaguely real-world example scenario, I’m going to develop a basic GPS coordinate-tracking Web application. It will generate randomised latitude-longitude pairs at irregular intervals. With some imagination, the coordinates generated could be the positions of nearby public transport, marathon runners carrying GPS devices, cars in a rally, or the location of a package in transit. The interesting part is how I tell the browser about the coordinates. Figure 1 shows a class diagram for this simple GPS-tracker application:
Figure 1. Class diagram showing major components of the GPS tracker application
UML class diagram of GPS tracker components

First, the application needs something that generates coordinates. This is what RandomWalkGenerator does. Starting from an initial coordinate pair, each call to its private generateNextCoord() method takes a random constrained step away from that location and returns the new position as a GpsCoord object. When initialized, RandomWalkGenerator creates a thread that calls the generateNextCoord() method at randomized intervals and then sends the generated coordinate to any CoordListenerinstances that have registered themselves with addListener()Listing 6 shows the logic of RandomWalkGenerator‘s loop:
Listing 6. RandomWalkGenerator’s run() method

                
public void run() {

  try {
    while (true) {
      int sleepMillis = 5000 + (int)(Math.random()*8000d);
      Thread.sleep(sleepMillis);
      dispatchUpdate(generateNextCoord());
    }
  } catch (Exception e) {
    throw new RuntimeException(e);
  }
}

 

CoordListener is a callback interface that just defines the onCoord(GpsCoord coord) method. In this example, theContinuationBasedTracker class implements CoordListener. The other public method on ContinuationBasedTrackeris getNextPosition(Continuation, int)Listing 7 shows the implementation of these methods:
Listing 7. The innards of ContinuationBasedTracker

                
public GpsCoord getNextPosition(Continuation continuation, int timeoutSecs) {

  synchronized(this) {
    if (!continuation.isPending()) {
      pendingContinuations.add(continuation);
    }

    // Wait for next update
    continuation.suspend(timeoutSecs*1000);
  }

  return (GpsCoord)continuation.getObject();
}

public void onCoord(GpsCoord gpsCoord) {

  synchronized(this) {
    for (Continuation continuation : pendingContinuations) {

      continuation.setObject(gpsCoord);
      continuation.resume();
    }

    pendingContinuations.clear();
  }
}

 

When a client calls getNextPosition() with a Continuation, the isPending method checks that the request is not being retried at this point, then adds it to a collection of Continuations that are waiting for a coordinate. Then the Continuation is suspended. Meanwhile, onCoord — invoked when a new coordinate is generated — simply loops over any pendingContinuations, sets the GPS coordinate on them, and resumes them. Each retried request then completes execution ofgetNextPosition(), retrieving the GpsCoord from the Continuation and returning it to the caller. Note the need for synchronization here, both to guard against inconsistent state in the pendingContinuations collection and to ensure that a newly added Continuation isn’t resumed before it has been suspended.

The final piece of the puzzle is the servlet code itself, shown in Listing 8:
Listing 8. GPSTrackerServlet implementation

                
public class GpsTrackerServlet extends HttpServlet {

    private static final int TIMEOUT_SECS = 60;
    private ContinuationBasedTracker tracker = new ContinuationBasedTracker();

    public void service(HttpServletRequest req, HttpServletResponse res)
                                                throws java.io.IOException {

      Continuation c = ContinuationSupport.getContinuation(req,null);
      GpsCoord position = tracker.getNextPosition(c, TIMEOUT_SECS);

      String json = new Jsonifier().toJson(position);
      res.getWriter().print(json);
    }
}

 

As you can see, this servlet does very little. It simply obtains the request’s Continuation, calls getNextPosition(), converts the GPSCoord into JavaScript Object Notation (JSON), and writes it out. Nothing here needs protection from reexecution, so I don’t need to check isPending()Listing 9 shows the output of a call to the GpsTrackerServlet, again with five simultaneous requests but only a single available thread on the server:
Listing 9. Output of GPSTrackerServlet

                
$  for i in 'seq 1 5'  ; do lynx -dump localhost:8080/tracker &  done
   { coord : { lat : 51.51122, lng : -0.08103112 } }
   { coord : { lat : 51.51122, lng : -0.08103112 } }
   { coord : { lat : 51.51122, lng : -0.08103112 } }
   { coord : { lat : 51.51122, lng : -0.08103112 } }
   { coord : { lat : 51.51122, lng : -0.08103112 } }

 

This example is unspectacular but serves as a proof-of-concept. After the requests are dispatched, they are held open for several seconds until the coordinate is generated, at which point the responses are quickly generated. This is the basis of the Comet pattern, with Jetty handling five concurrent requests on one thread, thanks to Continuations.

Back to top

Creating a Comet client

Now that you’ve seen how Continuations can be used in principle to create nonblocking Web services, you might wonder how to create client-side code to exploit this ability. A Comet client needs to:

  1. Hold open an XMLHttpRequest connection until a response is received.
  2. Dispatch that response to the appropriate JavaScript handler.
  3. Immediately establish a new connection.

A more advanced Comet setup could use one connection to push data from several different services to the browser, with appropriate routing mechanisms on the client and server. One possibility would be to write client-side code against a JavaScript library such as Dojo, which provides Comet-based request mechanisms in the shape of dojo.io.cometd.

However, if you’re working with the Java language on the server, a great way to get advanced Comet support on both client and server is to use the DWR 2 (see Resources). If you’re not familiar with DWR, you can read Part 3 of this series, “Ajax with Direct Web Remoting.” DWR transparently provides an HTTP-RPC transport layer, exposing your Java objects to calls across the Web from JavaScript code. DWR generates client-side proxies, automatically marshalls and unmarshalls data, handles security concerns, provides a convenient client-side utility library, and works in all major browsers.

Back to top

DWR 2: Reverse Ajax

Newly introduced with DWR 2 is the concept of Reverse Ajax. This is a mechanism by which server-side events are “pushed” to the client. Client-side DWR code transparently deals with establishing connections and parsing responses, so from a developer’s point of view, events can simply be published to the client from server-side Java code.

DWR can be configured to use three different mechanisms for Reverse Ajax. One is the familiar polling approach. The second, known as piggyback, doesn’t create any connections to the server. Instead, it waits until another DWR service call occurs and piggybacks pending events onto this request’s response. This makes it highly efficient but means that client notification of events is delayed until the client makes an unrelated call. The final mechanism uses long-lived, Comet-style connections. And best of all, DWR can auto-detect when it’s running under Jetty and switch to using Continuations for nonblocking Comet.

I’ll adapt my GPS example to use Reverse Ajax with DWR 2. On the way, you’ll see in more detail how Reverse Ajax works.

I no longer need my servlet. DWR provides a controller servlet that mediates client requests directly onto Java objects. I also no longer need to deal explicitly with Continuations because DWR takes care of this under the hood. So I simply need a newCoordListener implementation that publishes coordinate updates to any client browsers.

An interface called ServerContext provides DWR’s Reverse Ajax magic. ServerContext is aware of all Web clients currently viewing a given page and can provide a ScriptSession to talk to each. This ScriptSession is used to push JavaScript fragments to the client from Java code. Listing 10 shows how the ReverseAjaxTracker responds to coordinate notifications, using them to generate calls to the client-side updateCoordinate() function. Note that the appendData() call on the DWRScriptBuffer object automatically marshalls a Java object to JSON, if a suitable converter is available.
Listing 10. The notification callback method in ReverseAjaxTracker

                
public void onCoord(GpsCoord gpsCoord) {

  // Generate JavaScript code to call client-side
  // function with coord data
  ScriptBuffer script = new ScriptBuffer();
  script.appendScript("updateCoordinate(")
    .appendData(gpsCoord)
    .appendScript(");");

  // Push script out to clients viewing the page
  Collection<ScriptSession> sessions = 
            sctx.getScriptSessionsByPage(pageUrl);

  for (ScriptSession session : sessions) {
    session.addScript(script);
  }   
}

 

Next, DWR must be configured to know about ReverseAjaxTracker. In a larger application, DWR’s Spring integration could be leveraged to provide DWR with Spring-created beans. Here, however, I’ll just have DWR create a new instance ofReverseAjaxTracker and place it in the application scope. All subsequent DWR requests will then access this single instance.

I also need to tell DWR how to marshall data from GpsCoord beans into JSON. Because GpsCoord is a simple object, DWR’s reflection-based BeanConverter is sufficient. Listing 11 shows the configuration for ReverseAjaxTracker:
Listing 11. DWR configuration for ReverseAjaxTracker

                
<dwr>
   <allow>
      <create creator="new" javascript="Tracker" scope="application">
         <param name="class" value="developerworks.jetty6.gpstracker.ReverseAjaxTracker"/>
      </create>

      <convert converter="bean" match="developerworks.jetty6.gpstracker.GpsCoord"/>
   </allow>
</dwr>

 

The create element’s javascript attribute specifies a name that DWR uses to expose the tracker as a JavaScript object. However, in this case, my client-side code won’t use it, instead having data pushed to it from the tracker. Also, some extra configuration in web.xml is needed to configure DWR for Reverse Ajax, as shown in Listing 12:
Listing 12. web.xml configuration for DwrServlet

                
<servlet>
   <servlet-name>dwr-invoker</servlet-name>
   <servlet-class>
      org.directwebremoting.servlet.DwrServlet
   </servlet-class>
   <init-param>
      <param-name>activeReverseAjaxEnabled</param-name>
      <param-value>true</param-value>
   </init-param>
   <init-param>
      <param-name>initApplicationScopeCreatorsAtStartup</param-name>
      <param-value>true</param-value>
   </init-param>
   <load-on-startup>1</load-on-startup>
</servlet>

 

The first servlet init-paramactiveReverseAjaxEnabled, activates polling and Comet functionality. The second,initApplicationScopeCreatorsAtStartup, tells DWR to initialize the ReverseAjaxTracker at application startup time. This overrides the usual behaviour of lazy initialization when the first request on a bean is made — necessary in this case, because the client never does actively call a method on the ReverseAjaxTracker.

Finally, I need to implement the client-side JavaScript function invoked from DWR. The callback — updateCoordinate() — is passed a JSON representation of a GpsCoord Java bean, auto-serialized by DWR’s BeanConverter. The function just extracts the longitude and latitude fields from the coordinate and appends them to a list via Document Object Model (DOM) calls. This is shown in Listing 13, along with my page’s onload function. The onload contains a call todwr.engine.setActiveReverseAjax(true), which tells DWR to open a persistent connection to the server and await callbacks.
Listing 13. Client-side implementation of trivial Reverse Ajax GPS tracker

                
window.onload = function() {
  dwr.engine.setActiveReverseAjax(true);
}

function updateCoordinate(coord) {
  if (coord) {
    var li = document.createElement("li");
    li.appendChild(document.createTextNode(
            coord.longitude + ", " + coord.latitude)
    );
    document.getElementById("coords").appendChild(li);
  }
}

 

Updating the page without JavaScript

If you want to minimize the amount of JavaScript code in your application, there’s an alternative to writing out JavaScript callbacks with ScriptSession: You can wrap ScriptSession instances in a DWR Util object. This class provides simple Java methods for manipulating the browser DOM directly, and it auto-generates the necessary script behind the scenes.

Now I can point my browser to the tracker page, and DWR will begin pushing coordinate data to the client as it is generated. This implementation simply outputs a list of the generated coordinates, as shown in Figure 2:
Figure 2. Output of the ReverseAjaxTracker
Simple Web page listing generated coordinates

That’s how simple it is to create an event-driven Ajax application using Reverse Ajax. And remember, thanks to DWR’s exploitation of Jetty Continuations, no threads are tied up on the server while the client is waiting for a new event to arrive.

From here, it’s easy to integrate a map widget from the likes of Yahoo! or Google. By changing the client-side callback, coordinates can simply be passed to the map API, instead of appended directly onto the page. Figure 3 shows the DWR Reverse Ajax GPS tracker plotting the random walk on such a mapping component:
Figure 3. ReverseAjaxTracker with a map UI
Map showing path tracing generated coordinates

Back to top

Conclusions

You’ve now seen how Jetty Continuations combined with Comet can provide an efficient, scalable solution for event-driven Ajax applications. I haven’t given any figures for the scalability of Continuations because performance in a real-world application depends on so many variables. Server hardware, choice of operating system, JVM implementation, Jetty configuration, and indeed your Web application’s design and traffic profile all affect the performance of Jetty’s Continuations under load. However, Greg Wilkins of Webtide (the main Jetty developers) has published a white paper on Jetty 6 that compares the performance of a Comet application with and without Continuations, handling 10,000 concurrent requests (see Resources). In Greg’s tests, using Continuations cuts thread consumption, and concomitantly stack memory consumption, by a factor of more than 10.

You’ve also seen how easy it is to implement an event-driven Ajax application using DWR’s Reverse Ajax technology. Not only does DWR save you much client- and server-side coding, but Reverse Ajax also abstracts the whole server-push mechanism away from your code. You can switch freely among the Comet, polling, or even piggyback methods, simply by altering DWR’s configuration. You’re free to experiment and find the best-performing strategy for your application, without any impact on your code.

If you’d like to experiment with your own Reverse Ajax applications, a great way to learn more is to download and examine the code of the DWR demos (part of the DWR source-code distribution, see Resources). The sample code used in this article is also available (see Download) if you’d like to run the examples for yourself.

 

Back to top

Download

Description Name Size Download method
Sample code jetty-dwr-comet-src.tgz 8KB HTTP

Information about download methods

 

Resources

Learn

Get products and technologies

  • Jetty: Download Jetty.
  • DWR: Download DWR.

Discuss

About the author

Philip McCarthy is a London-based software-development consultant specializing in Java and Web technologies. Past work includes projects for Orange and Hewlett Packard Labs. His current focus is Web-based financial systems built with open source frameworks.

original:http://www.ibm.com/developerworks/java/library/j-jettydwr/index.html