A useful GWT RPC pattern I’ve been using

GWT RPC calls can wind up all over your code, doing all sorts of weird things. If you’re not careful about how you code them you can end up with bugs and other problems cropping up. The other part of RPC calls is that some of them are actually recoverable, and may even be retried a few times before giving up.

I developed the following little bit of code recently that may be useful to anyone doing RPC in GWT:

public abstract class RetryAction<T> implements AsyncCallback<T> {
    public abstract void attempt();
    public abstract void oncapture(T value);

    public void onFailure(Throwable error) {
        try {
            throw error;
        } catch(InvocationException invocationException) {
            Window.alert("A fatal error occurred, you should login " +
                "again or contact Technical Support");
        } catch(IncompatibleRemoteServiceException remoteServiceException) {
            Window.alert("A fatal error occurred, you should login " +
                "again or contact Technical Support");
        } catch(SerializationException serializationException) {
            Window.alert("A fatal error occurred, you should login " +
                "again or contact Technical Support");
        } catch(Throwable throwable) {
            String message = throwable.getLocalizedMessage();

            if(message == null) {
                message = throwable.getMessage();
            }

            if(message == null) {
                message = throwable.getClass().getName();
            }

            if(Window.confirm("An error occured:\n" + message + "\n" +
                "You may retry this operation by clicking 'OK'.\n" +
                "However if the error persists, contact Technical Support.")) {

                attempt();
            }
        }
    }

    public void onSuccess(T value) {
        try {
            oncapture(value);
        } catch(RuntimeException error) {
            onFailure(error);
        }
    }
}

The way this structure works is that you implement your RPC call in the “attempt” method, and the callback in the “oncapture” method. If a “non-fatal” error occurred, the user is given the opportunity to “Retry” the operation (resulting in another call to “attempt”). The pattern groups the RPC invocation nicely with it’s response logic. Here’s a little example:

public class GetContactListAction extends RetryAction<Contacts&#91;&#93;> {
    private ContactList contacts;

    public GetContactListAction(ContactList display) {
        contacts = display;
    }

    public void attempt() {
        CONTACT_LIST_SERVICE_ASYNC.getContactList(this);
    }

    public void oncapture(Contact[] data) {
        contacts.populate(data);
    }
}

And then to use this action:

GetContactListAction action = new GetContactListAction(contactList);
action.attempt();
Advertisements

8 Responses to “A useful GWT RPC pattern I’ve been using”

  1. David Browne Says:

    I admit I am kind of new at this, but in onSuccess() on line 41, calling onFailure() will retry attempt(), but shouldn’t you be retrying oncapture()?

  2. Jason Says:

    Hi David,

    onFailure() is normally run by the Async Service generated by GWT. The fact that I also invoke it in oncapture() is a convenience structure. oncapture() should almost never actually throw an Exception. If it does, the data returned is almost certainly corrupt somehow. Hence calling attempt() again, instead of an endless loop trying to get oncapture() to work.

    If onFailure() is called because of a server side error, we may (if the error appears not to be network related) give the user the option to try again, invoking attempt() again and starting the whole cycle over.

    Hope that explains the logic a bit more. 😉

  3. David Browne Says:

    Thanks Jason, I think I get it now. So oncapture() is intended to really just have simple logic that won’t lead to an exception unless the data is corrupted.

  4. Jason Says:

    Exactly. Generally what you want to do with onSuccess() (in a normal GWT AsynCallback) or in oncapture() is assign a field higher and display something.

    Async Requests are almost always (with the exception of retrieval polling) at the users request, so it follows: you want to give the user some feedback about what they’ve done or present them with the information they asked for. 😉

    In the case of presenting the data, I find the most easily read pattern to be:

    Assign the Data to a field
    Add a DeferredCommand to actually take care of the rendering

    I’ll be writing a post on different ways of effectively dealing with data from your server (without causing “Script running to long” dialogs and such) in a few days.

  5. Eli Says:

    Nice code! Shiny clean. I’ll probably be using this pattern going forward… still in the “let’s throw that against the wall and see if it sticks” phase so things are inevitably jumbled.

  6. William Says:

    Nice. Two tips:

    In the alert, put an image (like an error icon or a 1-pixel gif) that you call with parameters that describe the error.

    As part of the “contact support” message, include a mailto link with some explanatory text and the same data you pass via the error image.

    Those can let you get hard stats on how many people are getting errors. I also like to include a semi-unique number, just so I can correlate the mailed reports with the ones in the logs.

  7. Jason Says:

    @William

    I personally use my own little MessageBox class with an error icon on the left, mainly cause I don’t like making use of Window.alert/confirm/etc. Also allows you to theme the dialog to fit in with the rest of your application.

    On the semi-unique number thing, I used that technique a few years back in a normal web-app. When an Exception was caught, we’d log all the Session data, request data, and much of the application state to a text file on the disk. The file-name was the same as the “reference number” we displayed in the error message. Meant that when the support team got calls we could track almost exactly what had gone wrong.

  8. stefano Says:

    Hi Jason,
    i like this pattern and i’m trying to implement it in my little project. I’m new in GWT and RPC.
    Can u tell me if thi is correct?
    in “attempt()” i try to instantiate the service (GWT.create and setServiceEntryPoint) then i check if it’s all correct so i call “oncapture()” else call “onFailure()”.

    thanks.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: