Skip to content

Instantly share code, notes, and snippets.

@chumpy
Created January 28, 2012 23:28
Show Gist options
  • Save chumpy/1696249 to your computer and use it in GitHub Desktop.
Save chumpy/1696249 to your computer and use it in GitHub Desktop.
Queue RPC requests in GWT

Queue RPC Requests in GWT

The Problem

One of the handier tools in the GWT toolbox is a built in AJAX framework referred to as RPC or Remote Procedure Calls. Here’s the thing: Internet Explorer version 7 and prior is not awesome. IE pre-8 restricts the number of simultaneous XmlHttp requests to two for a given host. In most cases, this restriction doesn’t pose a real problem. For a rich client application (like the kind GWT is a good choice for) that may be leaving one of those two allowable connections open for a long poll while it fires off several RPCs with the other connection, this restriction can cause serious problems up to and including crashing the browser (trust me, I’ve crashed IE 7 several times this way while testing).

The Answer

The answer to the connection restriction is of course optimizing the async calls you make to the server. There are two basic ways to go about optimizing the async calls that have run a muck:

  1. Collapse several calls into one multipurpose call
  2. Queue the calls up so that they are executed in a serial manner

In general, I prefer the second choice as it allows the code relying on the async data to remain modular and not be tied to other data consumers in the application.
Here’s a way to queue up RPC calls in GWT so that you can predict how many simultaneous connections are in use regardless of the organization of the calls themselves.

Step 1

Create a singleton to act as a single client for all RPCs. We need a singleton because there’s some data that’s gonna have to stick around, namely the request queue. You don’t have to worry about synchronizing the private new() call or getInstance() method because as we know the java script compiled from java will run in a single threaded environment.

Step 2

Throw two member variables on the singleton

//the request queue
private List queuedRequests = new ArrayList(); 
 
//tracking to provide serial behavior 
private boolean insideRequest = false;

QueuedRPCRequest encapsulates a queued request and contains four interesting members:

private List args = new ArrayList();
private String method;
private String service;
private AsyncCallback callback;

Step 3

Build the part that actually makes the call.

public void executeNextRPC() {
    if (insideRequest || queuedRequests.isEmpty()) {
        //if we're in the middle of a request or the queue is empty
        return;
    }
    insideRequest = true;
    QueuedRPCRequest request = queuedRequests.remove(queuedRequests.size() - 1);
    if (request.getService().equals(QueuedRPCRequest.LOGIN_SERVICE)) {
        executeLoginService(request);
    } 
} 

public void addRequestToQueue(QueuedRPCRequest request) {
    queuedRequests.add(request);
    executeNextRPC();
} 

private void executeLoginService(QueuedRPCRequest request) {
    LoginServiceGWTAsync loginService =
                              (LoginServiceGWTAsync) GWT.create(LoginServiceGWT.class);
    ((ServiceDefTarget) loginService).setRpcRequestBuilder(
                    new RpcRequestBuilderNowWithTimeout());
    if (request.getMethod().equals(QueuedRPCRequest.LOGIN)) {
        loginService.login((String) request.getArgs().get(0),
                    (String) request.getArgs().get(1), request.getCallback());
    }
}

As you can see, we are switching on first the service that is to be executed and then the method. We are relying on the fact that the browser executes java script in a single threaded manner by tracking if we are currently in the middle of a call with a boolean. Of course all RPC requests in your entire application need to funnel through this class so you should add more services and methods as the need arises.

Step 4

Call the singleton from anywhere in your client code

AsyncCallback<userDTO> callback = new AsyncCallback<userDTO>(){
     public void onSuccess(userDTOresult) {
         RPCClient.getInstance().setInsideRequest(false);
         try {
             //do login result handling
         } finally {
             RPCClient.getInstance().executeNextRPC();
         }
     }

    public void onFailure(Throwable caught) {
        RPCClient.getInstance().setInsideRequest(false);
        //do error handling
        RPCClient.getInstance().executeNextRPC();
    }
};
QueuedRPCRequest request = new QueuedRPCRequest();
request.setService(QueuedRPCRequest.LOGIN_SERVICE);
request.addArg(username.getText());
request.addArg(password.getText());
request.setCallback(callback);
request.setMethod(QueuedRPCRequest.LOGIN);
RPCClient.getInstance().addRequestToQueue(request); 
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment