Calling REST from GWT with a little bit of JQuery

by Zack Grossbart on December 21, 2009

Update: The techniques mentioned in this article became part of the Spiffy UI Framework for REST and GWT.

GWT is gaining in popularity as an alternative to JavaScript. It’s a little larger and a little more difficult to deal with, but it’s type safe and makes it much easier to write maintainable code.

GWT even extends that type safety to AJAX calls with a built-in RPC mechanism. It wasn’t until I passed an object from client to server with the ease of GWT that I truly understood its appeal.

And that’s why using REST with GWT drives me a little crazy. When you call REST endpoints from GWT you lose type safety and the nice RPC mechanism. You’re also left parsing JSON in GWT which is much clunkier than dealing with it directly in JavaScript. I’ve heard that Google is thinking of adding a REST mechanism for GWT-RPC, but I can’t see how it would ever handle generic JSON data from a generic REST endpoint.

For a new project I’m working on I created a simple pattern to make REST calls from GWT with AJAX and a little JQuery. You still have to parse the JSON, but you only do it in one place and you get a very simple type safe API at the other end. Let’s take a look.

A little bit of JQuery

We’ll start with the generic JQuery AJAX call $.ajax. This simple function is very flexible and even more powerful. It can get you in a little trouble if you aren’t careful.

GWT has a RequestBuilder object that also makes AJAX requests, but it doesn’t support HTTP PUT or DELETE which are commonly required in REST calls. Without them you’re stuck using the JAX-RS extension mechanism which isn’t widely supported by REST endpoints.

Let’s look at some code.

callREST: function(/*string*/ restUrl, /*string*/ data, /*string*/ method, /*string*/id) {
    if (!data) {
        data = "";
    }
    
    if (!method) {
        method = "GET";
    }
    
    jQuery.ajax( { 
        url: restUrl,
        type: method,
        data: data,
        processData: false,
        timeout: 100000,
        dataType: "text",
        success: function(res) {
            com_novell_idm_rpt.doRESTCallback(res, id);
        },
        error: function (XMLHttpRequest, textStatus, errorThrown) {
             if (XMLHttpRequest.responseText) {
                 com_novell_idm_rpt.doRESTErrorCallback(XMLHttpRequest.status, XMLHttpRequest.responseText, id);
             }
        }
    });
}

This generic wrapper to the JQuery ajax function gives us everything we need to call REST. It also provides a single entry point to all of our REST calls. That makes it easy to add security, logging, or debugging later.

Calling JQuery with GWT

Calling JQuery from GWT is simple with the GWT JavaScript Native Interface. The call looks like this:

private static native void doCallREST(String url, String data, String method, String id)/*-{
    $wnd.com_novell_idm_rpt.callREST(url, data, method, id);
}-*/;

It’s worth noting that all of this JavaScript is inside a Java block comment. That’s how a GWT Java file can contains JavaScript code and still be valid Java. Block comments aren’t a nice place to write code so I always try to make my JSNI code as small as possible.

Calling GWT from JQuery

The code we’ve looked at so far is enough to call a REST API. Now we need to get the response and pass it back to GWT. This is tricky since the response has to go out over the network and will take a little while. JQuery gives us a nice callback function to handle this called success. When that happens we need to call back into the GWT code so we can process the response. That’s where it gets a little complicated.

The problem is we can’t call directly from JQuery back to GWT. GWT has to set up the function for us. The first step is to define pointer placeholders for the functions.

var com_novell_idm_rpt = {
    doRESTCallback: null,
    doRESTErrorCallback: null,
    callREST: function(/*string*/ restUrl, /*string*/ data, /*string*/ method, /*string*/id) {
    ...

We set up the two variables doRESTCallback and doRESTErrorCallback to give our GWT code a place to add the functions.

Now that we have a placeholder we define a method in GWT to add the JavaScript functions there.

final class RESTility {
            
private static final RESTility RESTILITY = new RESTility();1
private final HashMap m_callbackMap = new HashMap();

static {
    createJSFunctions(RESTILITY);2
}

private static native void createJSFunctions(RESTility util)3 /*-{
    $wnd.com_novell_idm_rpt.doRESTCallback = function(res, id) {
        util.@com.novell.idm.rpt.client.client.rest.RESTility::restCallback(Ljava/lang/String;Ljava/lang/String;)(res, id);
    }
    
    $wnd.com_novell_idm_rpt.doRESTErrorCallback = function(status, res, id) {
        util.@com.novell.idm.rpt.client.client.rest.RESTility::restErrorCallback(Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;)(status, res, id);
    }
}-*/;

public void restCallback(String response, String id) {4
    RESTCallback callback = RESTILITY.m_callbackMap.remove(id);
    assert callback != null;
    
    callback.onSuccess(JSONParser.parse(response));
}

public void restErrorCallback(Integer status, String response, String id) {5
    RESTCallback callback = RESTILITY.m_callbackMap.remove(id);
    assert callback != null;
    
    callback.onError(status.intValue(), response);
}

There’s a lot going on here, let’s go through it step by step. We have a utility class called RESTility. I stole the name from the new YaST/Web project which is also part of Novell. This utility manages the round-trip to JQuery for us.

The RESTility class is mostly static methods, but we need an object instance so GWT can do the callback. We maintain one instance1 using a singleton model. We use that singleton instance to make the method call to create our JavaScript functions2.

The createJSFunctions method3 adds the functions we will call back from JQuery. It uses a complex syntax, but that is just so the GWT compiler knows how to generate the code it needs to call the restCallback4 and restErrorCallback5 methods.

The body of the doRESTCallback and doRESTErrorCallback function has that one long line with the @ symbol. This is the way we tell GWT how to call our GWT code from JavaScript. We reference the object name, then the package of our class, then the type of each of our arguments, and finally the argument values. I know this is a little complex, but we only need to do it twice.

Now our JQuery code can call back into GWT using a simple function call.

success: function(res) {
    com_novell_idm_rpt.doRESTCallback(res, id);
}

Callbacks

You might notice in the previous code that we have a HashMap and we’re passing around an ID. We want to make this mechanism available for multiple calls at once so we define the RESTCallback interface.

interface RESTCallback {
    public void onSuccess(JSONValue val);
    
    public void onError(int statusCode, String errorResponse);
}

This allows many objects to use our RESTility class and gives us a way to call them back. These kinds of callbacks are very common in JavaScript where you are dealing with network latency and don’t have a first class threading model.

So far our code path takes the following steps:

  1. Setup the GWT callback functions for JQuery
  2. Call the callREST JQuery function
  3. Call back the GWT code
  4. Return the JSON value of the REST call back to the listener

We now have a mechanism that will call REST from GWT, support HTTP PUT and DELETE, and get the JSON data back to GWT. The next step is to turn that JSON data into a type safe JavaBean.

Making JavaBeans from JSON

When the callback gets the JSON data it needs to turn it into a type safe JavaBean. This is an error-prone process so we only want to do it in one place in our code. That means we’ll need another layer of abstraction that just knows about objects and doesn’t pass JSON data out into the rest of our code.

public interface RESTObjectCallBack {
    public void success(T o);
    
    public void error(String message);
}

The RESTObjectCallBack interface gives us a type safe generic way to call back and pass our parsed object. Once we have this interface we can define a static method that takes the interface, parses the JSON, and returns the populated JavaBean.

It”s easy to combine the JavaBean and the JSON parsing into one class. I defined a class named Reports with the fields name, date, and time. It also defines a static function to parse the JSON and return the JavaBean called loadReports.

public static void loadReports(final RESTObjectCallBack callback) {
    RESTility.callREST("/IDMRPT-CORE/reports", new RESTCallback() {
            public void onSuccess(JSONValue val) {
                Reports reports = new Reports();
                
                JSONArray reportsArray = val.isArray();
                for (int i = 0; i < reportsArray.size(); i++) {
                    JSONObject rval = reportsArray.get(i).isObject();
                    Report report = new Report();
                    report.setName(rval.get("name").isString().stringValue());
                    report.setTime(rval.get("time").isString().stringValue());
                    report.setDate(rval.get("date").isString().stringValue());
                    
                    reports.addReport(report);
                    
                }
                
                callback.success(reports);
            }
            
            public void onError(int statusCode, String errorResponse) {
                callback.error(errorResponse);
            }
        });
}

Putting it all together

GWT-REST diagram

  1. The Reports bean calls RESTility.
  2. RESTility (written in GWT) calls callREST JQuery to execute the AJAX call.
  3. JQuery calls the REST endpoint.
  4. The end point returns data that is caught in the success function from JQuery.
  5. We call the GWT REST callback from JQuery.
  6. The GWT REST callback gives the final data back to the Reports JavaBean.

Does it work in the real world?

A big benefit of this mechanism is the support for HTTP PUT and DELETE. JQuery gives you access to these HTTP methods, but it requires support from the browser to make it work. So far I’ve tested this successfully with:

  • Firefox 2
  • Firefox 3
  • Firefox 3.5
  • Internet Explorer 7
  • Internet Explorer 8
  • Chrome 3
  • Safari 4.0.4

I’ve also tested it on Windows, Linux, and Mac. I don’t have a copy of IE 6 to test with, but if anyone else does please let me know how it turns out.

Is it worth it?

I know this mechanism seems a little convoluted, so let’s look at some of the benefits.

Typesafe code. Let’s not forget that this is all type safe. That means the GWT compiler can find many of our errors at compile time. That’s important because compile-time errors happen at 10:00 AM in your office and run-time errors happen at 3:00 AM in your customer’s server room.

PUT and DELETE support. Avoiding the JAX-RS extensions is worth a lot and most REST APIs require HTTP PUT and DELETE.

A simple API. Let’s not forget that this call can be used over and over again. So what does it look like to call a REST endpoint from GWT?

Reports.loadReports(new RESTObjectCallBack() {
    public void success(Reports reports) {
        Window.alert("our reports: " + reports);
    }
    
    public void error(String message) {
        Window.alert("error: " + message);
    }
});

That is all it takes. It’s even easier than calling GWT-RPC.

Should we just write the whole thing in JQuery? JQuery is the best technology for many applications, but many JQuery applications suffer from poor error handling with JSON data. This pattern gives us a single place to handle JSON data and turn it into a type safe object.

Writing JQuery code is fast, and that’s always appealing, but most of the cost of software comes from code maintenance. It doesn’t matter how fast you can write the code if you can’t change it easily.

Update, Removing the JavaScript

After some of the great responses to this article I did a little more investigation into GWT and I found two interesting facts.

1. When GWT converts JavaScript values into GWT objects it calls the JavaScript eval function. This is problematic because the eval function could run arbitraty JavaScript. From the JSLint documentation:

The eval function (and its relatives, Function, setTimeout, and setInterval) provide access to the JavaScript compiler. This is sometimes necessary, but in most cases it indicates the presence of extremely bad coding. The eval function is the most misused feature of JavaScript.

2. GWT RequestBuilder does support HTTP PUT and DELETE. It blocks them by default because of a Webkit bug on Safari on Mac, but that bug has been fixed. All you need to do is extends RequestBuilder to make this work.

class RESTRequestBuilder extends RequestBuilder {
    public RESTRequestBuilder(String method, String url) {
        super(method, url);
    }
}

Combining these two pieces of information I removed all of the JavaScript and implemented the REST call using RequestBuilder. This gives us more type safety, skips the eval function, and makes the code smaller.