Apr 4, 2013

Improving the GWT Async Callback

The core of GWT framework for async communication to the server is through the async callback interface. Its interface is rather simple, but unfortunately, as with a lot GWT development the simplicity is lost with the the amount of boiler plate code you are constantly writing. You have to write a service interface, an async service interface, service implementation, and a class (or anonymous class) that extends the AsyncCallback. Also, if you have any framework code you want to add to your async calls, that is just more boiler plate code you need to add and make sure that everyone follows/uses when writing the client side calls to the async callback.

Needless to say, in my 1.5 years of writing GWT applications, I’m sick and tired of boiler plate code. I know boiler plate code is a result of issues with the Java language, but it seems like with GWT it is even more than with other projects. If only Google was working on a Groovy to Javascript GWT Compiler. But, since I’m confined to using Java, I will look into one area to reduce boiler plate code, and that is with the asynchronous service calls.

What is the GWT Asynchronous Service Interface?

The Asynchronous Service Interface is nice and simple. GWT, as with other client side libraries, have hidden the complexity of writing your AJAX calls. It is done by extending, usually through an anonymous inner class, the AsyncCallback interface.

The interface is just this:

  1. public interface AsyncCallback<T> {
  2.   void onFailure(Throwable caught);
  3.   void onSuccess(T result);
  4. }

You then have two service interfaces the standard interface, and the async interface

Standard Service Interface

  1. @RemoteServiceRelativePath("gwt/MyService")
  2. public interface MyService {
  3.   MyData getData(String parameter);
  4. }

Async Service Interface
the return parameter is wrapped as a parameter in the AsyncCallback interface (shown above).

  1. public interface MyServiceAsync {
  2.   void getData(String parameter, AsyncCallback<MyData> callback);
  3. }

Calling the Service from GWT

  1. public class MyClass {
  3.   @Inject
  4.   private MyServiceAsync service;
  6.   public void someMethod() {
  7.     service.getData(new AsyncCallback<MyData>() {
  8.       void onSuccess(MyData data) {
  9.         //success code goes here.
  10.       }
  11.       void onFailure(Throwable throwable) {
  12.         // failure code goes here.
  13.       }
  14.     });
  15.   }
  16. }

The boiler-plate code

There is code that I end up writing around calling all of my AJAX services. I would like to add this code w/out having boiler-plate code all around my application.

  • I want to log each call including the time needed to make the call
  • I want certain exceptions to be handled in a uniform way across all of my calls
  • I want the ability to retry the asynchronous service call.

If we look at the logging, adding code an existing Async Callback is easy enough. However, when you have many anonymous classes and more complex logging; it all adds up.

  1. public class MyClass {
  3.   @Inject
  4.   private MyServiceAsync service;
  6.   public void someMethod() {
  7.     long start = System.currentTimeMillis();
  8.     service.getData(new AsyncCallback<MyData>() {
  9.       void onSuccess(MyData data) {
  10.         Log.debug("time : " + (System.currentTimeMillis() - start);
  11.         //success code goes here.
  12.       }
  13.       void onFailure(Throwable throwable) {
  14.         Log.debug("time (failure) : " + (System.currentTimeMillis() - start);
  15.         // failure code goes here.
  16.       }
  17.     });
  18.   }
  19. }

Now, every time I make a service call, I need to add this boilerplate code.

The GwtCallbackWrapper

The solution is to wrap the AsyncCallback in another class, and use that class to make your async calls. Extending the AsyncCallback interface doesn’t work, since you cannot wrap the method calls and you end up providing alternate methods, but it is an interface (everythings public) so extending it just makes things more confusing. Instead, create a wrapper class that has the same methods, but creates a simple AsyncCallback that ends up calling the methods on the wrapped class. Then add a new onCall(AsyncCallback callback) method that is used to start the invocation. Then you can swap out this new class for the AsyncCallback class and your anonymous inner classes are virtually unchanged. And by adding in the onCall method, it makes retries easy.

Here is the AsyncCallback replacing that wraps that actual AsyncCallback. Since this is an abstract class, you can limit the scope of onSuccess(), onFailure(), and the newly added method onCall() to be protected. When used as an anonymous inner class, the onCall() method will have access to local variables (as long as they are final) as well as any class variables.

  1. import com.google.gwt.user.client.rpc.AsyncCallback;
  3. public abstract class GwtCallbackWrapper<T> {
  4.   private AsyncCallback<T> asyncCallback = new AsyncCallback<T>() {
  5.     public void onSuccess(T result) {
  6.       GwtCallbackWrapper.this.onSuccess(result);
  7.     }
  8.     public void onFailure(Throwable t) {
  9.       GwtCallbackWrapper.this.onFailure(t);
  10.     }
  11.   };
  13.   protected final AsyncCallback<T> getAsyncCallback() {
  14.     return asyncCallback;
  15.   }
  17.   protected abstract void onSuccess(T t);
  18.   protected abstract void onFailure(Throwable throwable);
  19.   protected abstract void onCall(AsyncCallback<T> callback);
  21.   public final void call() {
  22.     onCall(getAsyncCallback());
  23.   }
  24. }

Now, I can easily add in my aspect like behavior w/out really changing the API to the user (the onFailure() and onSuccess() method names and signatures remain unchanged).

  1. import com.allen_sauer.gwt.log.client.Log;
  2. import com.google.gwt.user.client.rpc.AsyncCallback;
  4. public abstract class GwtCallbackWrapper<T> {
  6.   private long start;
  8.   private AsyncCallback<T> asyncCallback = new AsyncCallback<T>() {
  9.     public void onSuccess(T result) {
  10.       Log.debug("Time : " + (System.currentTimeMillis() - start));
  11.       GwtCallbackWrapper.this.onSuccess(result);
  12.     }
  14.     public void onFailure(Throwable t) {
  15.       Log.debug("Time (failure) : " + (System.currentTimeMillis() - start));
  16.       GwtCallbackWrapper.this.onFailure(t);
  17.     }
  18.   };
  20.   protected final AsyncCallback<T> getAsyncCallback() {
  21.     return asyncCallback;
  22.   }
  24.   protected abstract void onSuccess(T t);
  26.   protected abstract void onFailure(Throwable throwable);
  28.   protected abstract void onCall(AsyncCallback<T> callback);
  30.   public final void call() {
  31.     start = System.currentTimeMillis();
  32.     onCall(getAsyncCallback());
  33.   }
  34. }

When I need to make an asynchronous call, I just use my wrapper class, never using the AsyncCallback directly.

  1. public class MyClass {
  3.   @Inject
  4.   private MyServiceAsync service;
  6.   public void someMethod(final String parameter) {
  7.     new GwtCallbackWrapper<MyData>() {
  8.       public void onCall(AsyncCallback<MyData> callback) {
  9.         service.call(parameter, callback);
  10.       }
  11.       void onSuccess(MyData data) {
  12.         //success code goes here.
  13.       }
  14.       void onFailure(Throwable throwable) {
  15.         // failure code goes here.
  16.       }
  17.     }).call();
  18.   }
  19. }

Don’t forget you will need to make any parameter or local variable of someMethod() to be final.

Now it is easy to add in even more “aspects” to the asynchronous calls. Lets say if you get 500 error back, you want to give the user a retry dialog.


  1. import com.allen_sauer.gwt.log.client.Log;
  2. import com.google.gwt.user.client.rpc.AsyncCallback;
  3. import com.google.gwt.user.client.rpc.StatusCodeException;
  5. public abstract class GwtCallbackWrapper<T> {
  7.   private long start;
  9.   private AsyncCallback<T> asyncCallback = new AsyncCallback<T>() {
  10.     public void onSuccess(T result) {
  11.       Log.debug("Time : " + (System.currentTimeMillis() - start));
  12.       GwtCallbackWrapper.this.onSuccess(result);
  13.     }
  15.     public void onFailure(Throwable t) {
  16.       Log.debug("Time (failure) : " + (System.currentTimeMillis() - start));
  17.       if (t instanceof StatusCodeException) {
  18.         StatusCodeException e = (StatusCodeException) t;
  19.         if (e.getStatusCode() >= 500) {
  20.           new MyDialog<T>(GwtCallbackWrapper.this).center();
  21.         } else {
  22.           GwtCallbackWrapper.this.onFailure(t);
  23.         }
  24.       } else {
  25.         GwtCallbackWrapper.this.onFailure(t);
  26.       }
  27.     }
  28.   };
  30.   protected final AsyncCallback<T> getAsyncCallback() {
  31.     return asyncCallback;
  32.    }
  34.   protected abstract void onSuccess(T t);
  36.   protected abstract void onFailure(Throwable throwable);
  38.   protected abstract void onCall(AsyncCallback<T> callback);
  40.   public final void call() {
  41.     start = System.currentTimeMillis();
  42.     onCall(getAsyncCallback());
  43.   }
  44. }

It can “retry” the service call, by just calling the wrapper.call() method.

  1. public class MyDialog<T> extends DialogBox {
  2.   public MyDialog(final GwtCallbackWrapper<T> wrapper) {
  3.     setText("Server not available, retry?");
  4.     Button ok = new Button("OK");
  5.     ok.addClickHandler(new ClickHandler() {
  6.       public void onClick(ClickEvent event) {
  7.         MyDialog.this.hide();
  8.         wrapper.call();
  9.       }
  10.     });
  11.     Button quit = new Button("Quit");
  12.     quit.addClickHandler(new ClickHandler() {
  13.       public void onClick(ClickEvent event) {
  14.         MyDialog.this.hide();
  15.       }
  16.     });
  17.     HorizontalPanel panel = new HorizontalPanel();
  18.     panel.add(ok);
  19.     panel.add(quit);
  20.     setWidget(panel);
  21.   }
  22. }

By wrapping the actual GWT AsyncCallback and using the wrapping class as your base class to your anonymous classes you create for your callbacks, you have any easy way to control the common code from your GWT client code, at least when it comes to making your service calls.

About the Author

Neil Buesing profile.

Neil Buesing

VP - Streaming Technologies

Neil has more than twenty-five years of Object-Oriented development experience, with twenty years in Java/J2EE application development. He has successfully delivered in the role of an architect and as lead developer.

Over the past 2 years, he has lead up the Real-Time Data Practice at Object Partners. He is a Kafka Architect, Developer, and Advocate presenting at multiple Kafka Summits and Meetups.

One thought on “Improving the GWT Async Callback

  1. Kabal says:

    Thanks. I’ve been searching it for some time.

  2. Paul says:

    Neil, this is a great article. Working through this problem with the example is ideal. Re-jigging the way AsynCallback callbacks are used becomes pretty important with more complex applications. This helped me take a new approach.

Leave a Reply

Your email address will not be published. Required fields are marked *

Related Blog Posts
AWS Cloud HSM, Docker and NGINX
There is quite a bit of easily searchable content on the security benefits of leveraging a Hardware Security Module to manage cryptographic keys, so I will leave that to the scope of another article. The […]
Google Professional Machine Learning Engineer Exam 2021
Exam Description A Professional Machine Learning Engineer designs, builds, and productionizes ML models to solve business challenges using Google Cloud technologies and knowledge of proven ML models and techniques. The ML Engineer is proficient in all aspects […]
Designing Kubernetes Controllers
There has been some excellent online discussion lately around Kubernetes controllers, highlighted by an excellent Speakerdeck presentation assembled by Tim Hockin. What I’d like to do in this post is explore some of the implications […]
React Server Components
The React Team recently announced new work they are doing on React Server Components, a new way of rendering React components. The goal is to create smaller bundle sizes, speed up render time, and prevent […]