TurboManage

David Chandler's Journal of Java Web and Mobile Development

  • David M. Chandler


    Web app developer since 1994 and former Developer Advocate with Google now residing in Colorado. Besides tech, I enjoy landscape photography and share my work at ColoradoPhoto.gallery.

  • Subscribe

  • Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 242 other followers

  • Sleepless Nights…

    July 2010
    S M T W T F S
    « May   Aug »
     123
    45678910
    11121314151617
    18192021222324
    25262728293031
  • Blog Stats

    • 848,973 hits

A DispatchQueue to sequence RPC calls with gwt-dispatch

Posted by David Chandler on July 16, 2010

One of GWT’s greatest strengths, but one that took some getting used to for this long-time server-side developer, is that it forces an asynchronous programming model. Because the current generation of JavaScript engines are single-threaded, GWT is designed to prohibit any code from blocking the thread. GWT ensures this through extensive use of callbacks, and it’s generally a good thing as it helps keep the browser responsive to user input. This is especially important for RPC calls. If GWT were to allow synchronous RPC calls, a long-running server call would hang the browser.

One of the challenges of asynchronous programming, however, is that you do not know the order in which callbacks will be called. In some cases, this is helpful. For example, in order to minimize trips to the Datastore and do as much processing as possible on the client, I do some data joins on the client. I load the list of parent entities once and cache it (see previous post), then join various child entities on successive calls as needed. In order for this to work, I need to know that the parent entity is present in the client when the child entities are retrieved. That is, when the call to retrieve child entities completes, the parent entities must already be loaded so each child entity can look up properties of the parent entity.

There are various ways to ensure that related things happen together. One way is to use a single Action to retrieve both parent and child entities and populate a compound Result object. However, this is somewhat wasteful as the list of parent entities must be retrieved and sent on each request for related child entities. Likewise, doing the join on the server results in some parent entity data being transmitted multiple times. If you could make synchronous calls (as on the server), you would just make the calls in order: 1) retrieve parent entities (from cache if available), and 2) retrieve child entities. But we can’t make synchronous RPC calls, for good reason as explained previously.

What to do? We cannot determine the order in which callbacks will be invoked for multiple requests made at the same time. However, we can ensure that a request is not even initiated until another call has completed. The trick is to initiate the request within the onSuccess() block of the prerequisite call. We’ll call this callback chaining, and it looks like this:

	void chainCallbacks()
	{
		dispatch.execute(new PrerequisiteAction(), new AsyncCallback<PrerequisiteResult>()
		{
			@Override
			public void onFailure(Throwable caught)
			{
				// TODO Auto-generated method stub
			}

			@Override
			public void onSuccess(PrerequisiteResult result)
			{
				dispatch.execute(new DependentAction(), new AsyncCallback<DependentResult>()
				{
					@Override
					public void onFailure(Throwable caught)
					{
						// TODO Auto-generated method stub
					}

					@Override
					public void onSuccess(DependentResult result)
					{
						// Do something dependent on PrerequisiteResult 
					}
				});
			}
		});
	}

Besides the horror of nested anonymous inner classes, this approach is less than ideal because it introduces latency. The second request cannot begin until the first one has completed. Thus you lose the benefit of being able to execute many requests simultaneously on your server farm. But wait, you say, wasn’t that the whole point? Not exactly. It turns out that our real requirement is not that calls are initiated in a prescribed order, but rather just that our callbacks are invoked in a prescribed order. Is there a way to fire off all requests simultaneously (subject to browser limitations, of course) and still invoke callbacks in the order we want?

We can achieve this by wrapping each callback in another callback that is smart enough to hold the result until all callbacks are completed. I call this the DispatchQueue. Here’s the source for DispatchQueue and CallbackCommand (which is a useful abstraction to hold an Action and its callback together):

DispatchQueue.java

package com.turbomanage.gwt.client.dispatch;

import java.util.ArrayList;
import java.util.HashMap;

import net.customware.gwt.dispatch.client.DispatchAsync;
import net.customware.gwt.dispatch.shared.Action;
import net.customware.gwt.dispatch.shared.Result;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.AsyncCallback;

/**
 * Sequentially orders commands (service calls) by initiating all requests, then
 * invoking callbacks in order.
 * 
 * @author David Chandler
 */
public class DispatchQueue
{
	private DispatchAsync dispatch;
	private ArrayList<CallbackCommand> commands = new ArrayList<CallbackCommand>();
	private HashMap<Action<Result>, Object> outcomes = new HashMap<Action<Result>, Object>();
	private int numPending;

	/**
	 * Constructor requires a dispatcher 
	 * 
	 * @param dispatch The dispatcher that will be used to handle requests
	 */
	public DispatchQueue(CachingDispatchAsync dispatch)
	{
		this.dispatch = dispatch;
	}

	/**
	 * Convenience method to queue a command with syntax identical to execute()
	 * 
	 * @param action
	 * @param callback
	 * @return
	 */
	public <A extends Action<R>, R extends Result> DispatchQueue add(final A action,
		final AsyncCallback<R> callback)
	{
		CallbackCommand cmd = new CallbackCommand(action, callback);
		return add(cmd);
	}

	/**
	 * Add a CallbackCommand to the queue. Works by wrapping the previous
	 * command's callback with a new callback that calls this command in it
	 * onSuccess() handler
	 * 
	 * @param cmd
	 */
	public DispatchQueue add(final CallbackCommand cmd)
	{
		// Block new entries once flushing has begun
		// if (started)
		if (numPending > 0)
			throw new UnsupportedOperationException("Queue is already running");
		// Wrap with new callback that decrements the counter and saves the
		// outcome
		final AsyncCallback<Result> origCallback = cmd.getCallback();
		AsyncCallback<Result> wrapper = new AsyncCallback<Result>()
		{
			@Override
			public void onFailure(Throwable caught)
			{
				if (numPending > 0)
				{
					// Pass through to original and call next command
					saveOutcome(cmd.getAction(), caught);
					if (--numPending == 0)
						finish();
				}
				else
				{
					origCallback.onFailure(caught);
				}
			}

			@Override
			public void onSuccess(Result result)
			{
				if (numPending > 0)
				{
					// Pass through to original and call next command
					saveOutcome(cmd.getAction(), result);
					if (--numPending == 0)
						finish();
				}
				else
				{
					origCallback.onSuccess(result);
				}
			}
		};
		cmd.setCallback(wrapper);
		commands.add(cmd);
		return this;
	}

	/**
	 * Execute queued commands and clear the queue upon completion.
	 * queueCommand() has already wrapped each callback with a chaining
	 * callback, so this method just has to call the first command.
	 */
	public void flush()
	{
		// Prohibit new entries once started
		this.numPending = commands.size();
		// This starts them all asynchronously, and callback
		// wrappers save the outcomes
		for (CallbackCommand cmd : commands)
		{
			dispatch.execute(cmd.getAction(), cmd.getCallback());
		}
	}

	private void saveOutcome(Action<Result> action, Object outcome)
	{
		outcomes.put(action, outcome);
	}

	private void finish()
	{
		GWT.log("Entering DispatchQueue.finish()", null);
		// Call each callback in sequence
		for (CallbackCommand cmd : commands)
		{
			Action<Result> action = cmd.getAction();
			AsyncCallback<Result> callback = cmd.getCallback();
			Object outcome = outcomes.get(action);
			if (outcome instanceof Result)
			{
				callback.onSuccess((Result) outcome);
			}
			else
			{
				callback.onFailure((Throwable) outcome);
			}
		}
	}
}

CallbackCommand.java

package com.turbomanage.gwt.client.dispatch;

import net.customware.gwt.dispatch.shared.Action;
import net.customware.gwt.dispatch.shared.Result;

import com.google.gwt.user.client.rpc.AsyncCallback;

/**
 * Wraps an action and callback so that they can be added to a DispatchQueue
 * 
 * @author David Chandler
 */
public class CallbackCommand
{
	private Action<Result> action;
	private AsyncCallback<Result> callback;

	@SuppressWarnings("unchecked")
	public CallbackCommand(final Action action, final AsyncCallback callback)
	{
		this.action = action;
		this.callback = callback;
	}

	public Action<Result> getAction()
	{
		return action;
	}

	public AsyncCallback<Result> getCallback()
	{
		return callback;
	}

	public void setCallback(AsyncCallback<Result> callback)
	{
		this.callback = callback;
	}
}

To use DispatchQueue, just create a new queue, add your actions and corresponding callbacks in order, and flush the queue:

	public void doInOrder()
	{
		new DispatchQueue(dispatch)
			.add(new PrerequisiteAction(), new AsyncCallback<PrerequisiteResult>()
			{
				@Override
				public void onFailure(Throwable caught)
				{
					// TODO Auto-generated method stub
				}

				@Override
				public void onSuccess(PrerequisiteResult result)
				{
					// Store results in model
				}
			})
			.add(new DependentAction(), new AsyncCallback<DependentResult>()
			{
				@Override
				public void onFailure(Throwable caught)
				{
					// TODO Auto-generated method stub
				}

				@Override
				public void onSuccess(DependentResult result)
				{
					// Do something that depends on prerequisite results
				}
			})
			// Do it
			.flush();
	}

DispatchQueue is probably one of the most fun pieces of code I’ve ever written. It combines the benefits of asynchronous requests with the determinism of synchronous calls. Used in conjunction with the caching dispatcher from the previous post, it is extremely powerful. Each time you need multiple call(back)s to be executed in order, just queue them up and go. The caching dispatcher returns each result from cache if available, and the dispatch queue ensures that callbacks are called in order . You’ll also find it helpful to store commonly used Action/callback pairs as CallbackCommands. For example, a service that always needs prerequisite data can store the CallbackCommand as a class field and add it to each queue as needed with DispatchQueue.add(CallbackCommand cmd).

Note that in this implementation of DispatchQueue, if any call fails, the onFailure() method for all callbacks will be called, passing the exception from the original failure. I’m not sure this is always the best behavior, but you’re welcome to change it ๐Ÿ™‚

Enjoy!

13 Responses to “A DispatchQueue to sequence RPC calls with gwt-dispatch”

  1. Martin said

    Hi David,

    thank you for your great work on gwt-dispatch.

    My question: is it planned to integrate the enhancements you blogged about in the gwt-dispatch project?

    TIA
    Martin

  2. Mirco said

    Hi David,

    Yeah, thank you for taking the time of writing and sharing this great piece of code!

    Though, I’ve got a question for you. I’m under the impression that the DispatchQueue is not doing what it promises, as I don’t see how it can enforce in-order delivery/execution of the batched actions.

    Let me elaborate on my thought:

    When DispacthQueue.flush() is called, the batched actions are executed one after the other, in paralel (by that I mean that we are not waiting for the result of an action before executing the next one).
    Now, if I have two actions, Foo and Bar, that go through the DispatchAsync.execute() in this order, due to the natural asynchrony of these client-server requests, I don’t see how it can be ensured that action Foo is actually executed before action Bar. I agree that most of time this will be the actual behavior, but let’s say that for some unkown (black magic:) reson the TCP packet that carries the bits of action Foo is delayed. Then, wouldn’t I be in a problematic situation where the handler of action Bar may be executed before the Foo’s handler? Which, if true, would break the contract between the DispatchQueue and the client’s code.

    Is there anything I am missing that makes my use case not possible in practice?

    Cheers,
    Mirco

    • Correct, neither the calls nor the real RPC callbacks are actually executed in order, but by wrapping the callbacks and waiting for them all to finish, DispatchQueue can then invoke in order the callbacks in client code. If server-side ordering is also needed, some other mechanism must be used.

  3. Subra said

    Don’t know if it would be useful, but it could be made greedier to execute the original callback as soon as it is available while respecting the order. Each command is given an index ‘i’ as a context variable in the wrapper. If the next callback to be processed is index ‘i’, then, when the callback i returns and i+1, i+2, .., j have already returned (and j+1 hasn’t) , they can be processed too.

    It might increase the throughput if you have long running (server side) tasks at the end of the dispatch queue.

    • Ooh, interesting idea! Thanks! If you’ve written this already and would like to share, feel free to email or post a link.

      • JP FIelding said

        i dont know if subra ever got back to you. i was playing with a unit test for the dispatch queue and decided to work for the immediate return. if you’re interested, let me know if you see anything bone headed that if missed. i skipped the rejection of new additions post flush and removed a few other pieces, but i believe the core is correct.

        public class DispatchQueue {

        private DispatchAsync dispatch;

        private final HashMap<Action, Object> stash = new HashMap<Action, Object>();
        private final LinkedHashMap pending = new LinkedHashMap();

        public DispatchQueue(DispatchAsync dispatch) {
        this.dispatch = dispatch;
        }

        public <A extends Action, R extends Result> DispatchQueue add(final A action, final AsyncCallback callback) {
        this.pending.put(action, callback);
        return this;
        }

        public void flush () {
        for (final Action a : this.pending.keySet()) {
        AsyncCallback wrapper = new AsyncCallback() {
        public void onFailure(Throwable caught) {
        onDone(a, caught);
        }
        public void onSuccess(Object result) {
        onDone(a, result);
        }
        };
        this.dispatch.execute(a, wrapper);
        }
        }

        protected void onDone(Action action, Object result) {
        if (action == this.pending.keySet().iterator().next()) {
        apply(this.pending.remove(action), result);
        if (this.pending.isEmpty()) return;
        Action next = this.pending.keySet().iterator().next();
        if (!this.stash.containsKey(next)) return;
        onDone(next, this.stash.remove(next));
        } else {
        // we’re not next, so wait our turn
        this.stash.put(action, result);
        }
        }

        protected static void apply(AsyncCallback callback, Object outcome) {
        if (outcome instanceof Result) {
        callback.onSuccess(outcome);
        } else {
        callback.onFailure((Throwable) outcome);
        }
        }

        protected void finish() {
        // noop for callbacks
        }
        }

      • Thanks for following up, JP. I removed your follow-on comment as code formatting failed there also. Try

        ...
  4. Deep said

    I wanted to clarify something – can we assume with RequestFactory that a race condition won’t occur, and that queuing is already built in?

    For example, if .append() is used to chain together multiple requests (reqA, reqB, and reqC), can we rely on RequestFactory to execute the onSuccess() handlers of each request in the order they were chained, even if the server responds in a different order? It would seem so, since RequestFactory is using one connection to fulfill all the requests, right, and is then passing the results to the requests individually?

    • If by “chained,” you are referring to nested callbacks, then yes. Otherwise, I don’t think there is a guarantee that two separate requests will be called back in a particular order.

      • Deep said

        Thank you for responding so quickly – let me use some sample code to demonstrate. Let’s say that we have a popup to view car models from a list, and each car has its own set of options; the user can check off the options they like on each car, so those options should be checked the next time they view the car. Thus the popup must build a table of the options unique to that car, then must check off what has been saved.

        Originally I would have put the second request with its own fire() in the onSuccess of the first, to ensure the car options table was built in the DOM already; but the more efficient way would be to append it, as below. My question is if we can rely on setSavedOptions() to never get called before buildCarOptionsTable() has completed. (I realize this could all be solved by including the checked options as part of the original request, but this is for q&a purposes.)

        Request carReq = factory.getAppRequestFactory().carRequest();
        carReq.getCarOptions(selectedCarId).to(new Receiver<List>() {
        @Override
        public void onSuccess(List response) {
        factory.getCarListView().getCarPopup().buildCarOptionsTable(response);
        }
        });
        // next load user’s saved car options, if loadOptions is true
        if (loadOptions) {
        SavedCarOptionsRequest carOptReq = carReq.append(factory.getAppRequestFactory().savedCarOptionsRequest());
        carOptReq.findSavedOptionsByCarUser(selectedCarId, userId).to(new Receiver<List>() {
        @Override
        public void onSuccess(List response) {
        factory.getCarListView().getCarPopup().setSavedOptions(response);
        }
        });
        }
        carReq.fire();

        According to this post, it will always execute in sequence:

        http://groups.google.com/group/google-web-toolkit/browse_thread/thread/2e603cfd41ddad9f

        In my own manual testing, it seems to never have an issue, thus far; but I wanted to get an authoritative stance on it, and build in the necessary client logic if it is possible the onSuccess() methods will be called out of order.

      • I would be surprised if RequestFactory guarantees callback order for any reason, but Thomas Broyer is a much better authority on RequestFactory than I am ๐Ÿ™‚ Check the source and let us know what you find.

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: