TurboManage

David Chandler's Journal of Java Web and Mobile Development

  • David M. Chandler


    Web app developer since 1994 and Google Cloud Platform Instructor 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 230 other followers

  • Sleepless Nights…

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

    • 990,947 hits

Archive for July, 2010

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!

Advertisements

Posted in Google Web Toolkit | 13 Comments »

Caching, batching dispatcher for gwt-dispatch

Posted by David Chandler on July 12, 2010

In a previous post, I mentioned that I’d beefed up CachingDispatchAsync to do batching and queueing of commands (gwt-dispatch Action/Result pairs). I’m finally ready to publish this code. Today we’ll look at caching and batching, and tomorrow at queuing (or callback chaining).

First, let’s review caching. The basic idea is to save trips to the server by returning results from client-side cache when possible. We can use a simple marker interface to indicate whether an Action should be cacheable:

package com.turbomanage.gwt.client.dispatch;

/**
 * Marker interface for Action classes whose corresponding Results can be cached
 * 
 * @author David Chandler
 */
public interface Cacheable {

}

To enable caching, simply implement Cacheable in your Action class and override equals() and hashcode() as discussed in the earlier post. In this example, the FindUserListSubsAction implements Cacheable, so the caching dispatcher will always attempt to return a result from cache if available.

package com.roa.app.shared.rpc;

import com.turbomanage.gwt.client.dispatch.Cacheable;

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

public class FindUserListSubsAction implements Action<FindUserListSubsResult>, Cacheable
{

	public FindUserListSubsAction()
	{
		// Empty constructor for GWT-RPC
	}

	@Override
	public boolean equals(Object obj)
	{
		// All instances of this class should return the same cached Result
		return this.getClass().equals(obj.getClass());
	}

	@Override
	public int hashCode()
	{
		return this.getClass().hashCode();
	}

}

I debated marking Result classes as Cacheable rather than Action classes. It doesn’t matter as long as Action and Result classes are paired 1:1, but I ended up deciding on making Actions cacheable to remove ambiguity in the unlikely event that multiple Action classes return the same Result class.

The cache is implemented as a HashMap, where the key is an Action instance and the value is the corresponding Result. The dispatcher’s execute() method checks to see if an Action is Cacheable. If so, it will first try to return the result from cache. If no result is available, it will go ahead and call the dispatcher RPC service and cache the result. Because the cache uses an instance of an Action as the map key, you may have multiple results in the cache for a given Action class. For example, a FindUserByIdAction that takes a user ID argument will cache the result for each unique user ID (provided, of course, that you correctly override equals() and hashcode() on FindUserByIdAction).

Here’s my caching implementation of DispatchAsync:

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;
import com.google.inject.Inject;
import com.google.inject.Singleton;

/**
 * Dispatcher which supports caching of data in memory
 * 
 * In order for caching to work, Action classes must override
 * equals() and hashCode() appropriately! Alternatively, you can pass the
 * same instance of an Action with subsequent requests (i.e., use new
 * only once).
 */
@Singleton
public class CachingDispatchAsync implements DispatchAsync
{
	private DispatchAsync dispatcher;
	private static HashMap<Action<Result>, Result> cache = new HashMap<Action<Result>, Result>();
	private static HashMap<Action<Result>, ArrayList<AsyncCallback<Result>>> pendingCallbacks = new HashMap<Action<Result>, ArrayList<AsyncCallback<Result>>>();

	@Inject
	public CachingDispatchAsync(final DispatchAsync dispatcher)
	{
		this.dispatcher = dispatcher;
	}

	/**
	 * (non-Javadoc)
	 * 
	 * @see net.customware.gwt.dispatch.client.DispatchAsync#execute(A,
	 * com.google.gwt.user.client.rpc.AsyncCallback)
	 */
	public <A extends Action<R>, R extends Result> void execute(final A action,
			final AsyncCallback<R> callback)
	{
		if (action instanceof Cacheable)
		{
			executeWithCache(action, callback);
		}
		else
		{
			dispatcher.execute(action, callback);
		}
	}

	/**
	 * Execute the give Action. If the Action was executed before it will get
	 * fetched from the cache
	 * 
	 * @param Action
	 *            implementation
	 * @param Result
	 *            implementation
	 * @param action
	 *            the action
	 * @param callback
	 *            the callback
	 */
	@SuppressWarnings("unchecked")
	private <A extends Action<R>, R extends Result> void executeWithCache(
			final A action, final AsyncCallback<R> callback)
	{
		GWT.log("Executing with cache " + action.toString());
		final ArrayList<AsyncCallback<Result>> pending = pendingCallbacks.get(action);
		// TODO need a timeout here?
		if (pending != null)
		{
			GWT.log("Command pending for " + action, null);
			// Add to pending commands for this action
			pending.add((AsyncCallback<Result>) callback);
			return;
		}
		Result r = cache.get(action);

		if (r != null)
		{
			GWT.log("Cache hit for " + action, null);
			callback.onSuccess((R) r);
		}
		else
		{
			GWT.log("Calling real service for " + action, null);
			pendingCallbacks.put((Action<Result>) action, new ArrayList<AsyncCallback<Result>>());
			dispatcher.execute(action, new AsyncCallback<R>()
			{
				public void onFailure(Throwable caught)
				{
					// Process all pending callbacks for this action
					ArrayList<AsyncCallback<Result>> callbacks = pendingCallbacks.remove((Action<Result>) action);
					for (AsyncCallback<Result> pendingCallback : callbacks)
					{
						pendingCallback.onFailure(caught);
					}
					callback.onFailure(caught);
				}

				public void onSuccess(R result)
				{
					GWT.log("Real service returned successfully " + action, null);
					// Process all pending callbacks for this action
					ArrayList<AsyncCallback<Result>> callbacks = pendingCallbacks.remove((Action<Result>) action);
					for (AsyncCallback<Result> pendingCallback : callbacks)
					{
						pendingCallback.onSuccess(result);
					}
					cache.put((Action) action, (Result) result);
					callback.onSuccess(result);
				}
			});
		}
	}
	
	/**
	 * Clear the cache
	 */
	public void clear()
	{
		cache.clear();
	}

	/**
	 * Clear the cache for a specific Action
	 * 
	 * @param action
	 */
	@SuppressWarnings("unchecked")
	public <A extends Action<R>, R extends Result> void clear(A action)
	{
		cache.put((Action<Result>) action, null);
	}

}

To wire it up, simply bind it in your GIN module and inject it into your presenters or services.

In your GIN module:

		// for gwt-dispatch
		bind(CachingDispatchAsync.class);

In a presenter or service class:

	...
	private final EventBus eventBus;
	private final CachingDispatchAsync cachingDispatch;

	@Inject
	public MyServiceImpl(final EventBus eventBus, final CachingDispatchAsync dispatch)
	{
		this.cachingDispatch = dispatch;
		this.eventBus = eventBus;
	}
	...

If you wish, you can bind and inject CachingDispatchAsync as an implementation of DispatchAsync; however, if you do this, you won’t be able to call the clear() methods because they are not present on DispatchAsync.

One happy consequence of using a caching dispatcher is that it helps prevent what some have called “exploding event classes.” In the absence of a caching dispatcher, the most efficient way to supply the same data to multiple presenters is to fire a custom event containing a Result from an RPC call. Each presenter that needs the data can then listen for the event. The unhappy side effect of this is that you may end up with a custom event for each service call. With a caching dispatcher, this is no longer necessary. Each presenter can call the dispatcher “just in time,” and the dispatcher will return it from cache if available. Custom events are then only needed when multiple presenters must be notified immediately of changes to data.

Closely related to this, a caching dispatcher makes startup with multiple presenters much easier. Let’s say you have three presenters that all need the same data on initial load. Previously, you would have to use a custom event (or roll your own caching) to distribute the data to each presenter. Now, however, each presenter can call the dispatcher as if it were the only presenter, and the dispatcher will return the data from cache or a service call as needed.

Which brings us to batching. What happens if, during startup, three presenters all call dispatch.execute() for the same Action at the same time? The dispatcher can’t return a result from cache until at least one of the service calls has completed, so you will likely end up with all three dispatch requests resulting in RPC calls, thereby defeating the whole purpose of caching. Fortunately, the caching dispatcher above is smart enough to deal with this. Before it sends an Action over RPC, it checks to see if the same Action is already in progress. If so, it waits for the response and invokes all waiting callback methods for that Action with the Result.

The combination of caching and batching makes it possible to write network-efficient code quite simply. Tomorrow, we’ll look at queuing service calls to run in a particular order.

Posted in Google Web Toolkit | 13 Comments »

Configuring Eclipse on a Mac

Posted by David Chandler on July 8, 2010

I’m attempting to make the switch from Windows to Mac courtesy of my new employer (I’m pleased to be working with the GWT team at Google). While I very much like my shiny new MacBook Pro, I simply don’t get all the Mac hype. The biggest problem has been that I’m a keyboard person, and Macs are for mouse people. Most Windows keyboard shortcuts have Mac equivalents, but they’re just different enough to make for a bit of a learning curve, like Ctrl+arrows instead of Home and End. Thankfully, many keys can be remapped via System Preferences, but I haven’t found a way to map the editing keys yet (anyone?). My biggest annoyance is that holding down the Alt key doesn’t bring up menu shortcuts as on Windows. That makes going to Run | External Tools a real pain in Eclipse. Alt+R, E becomes Ctrl+F2,R,R,down arrow,E. Runner up annoyance is that there’s no way to use the keyboard to click “No” in a confirmation dialog.

At any rate, here are a few notes for other Eclipse users making the switch from Windows to Mac.

The first thing is to make the fonts readable. This is not necessarily a Mac thing, but the system font does seem especially small. In Eclipse preferences (Command+comma in most apps on the Mac–nice), I bumped up the font size (General | Appearance | Colors and Fonts | Basic | Text Font). This increases it for the Console window, but some plug-ins seem to use the system font instead, which leaves these “over the hill” eyes squinting at logs. Which, in turn, brings us to an almost-unbelievable discovery: you can’t change the system font on a latest, greatest Mac (OS X 10.6.3)! Problem still unsolved.

Now, time to move over my Eclipse external tools configurations from Windows. The external tools config to launch Finder in the selected project directory is very cool, but far from obvious. In the Run | External Tools | Configuration… dialog, enter the following information:

Location: /usr/bin/open
Working Directory: ${project_loc}
Arguments: .

This corresponds to typing “open .” in a Terminal window, which launches Finder in the current directory. Now if I could just configure a shortcut key to launch Terminal so I don’t have to click the icon in the dock every time… See Finder toolbar button to open Terminal to go the other way.

You can also create an external tool to launch Terminal in the selected project directory. Well, almost. To launch Terminal, you have to key into the Location field the full path /Applications/Utilities/Terminal.app/Contents/MacOS/Terminal; however, it always opens in your home directory, even if you set Working Directory to the ${project_loc} variable. That defeats the purpose of this particular tool launcher. Fortunately, xterm is a little smarter, so the following settings work:

Location: /usr/X11/bin/xterm
Working Directory: ${project_loc}
Arguments: (none)

Unfortunately, the default xterm font is miniscule.

Fortunately, you can put the following lines in ~/.Xdefaults to increase the default font size:

XTerm*font:     *-fixed-*-*-*-20-*
XTerm*boldFont: *-fixed-*-*-*-20-*

Now we’ve replicated the command prompt external tool from Windows Eclipse, but unfortunately, it’s a pain to get to the Run | External Tools menu via the keyboard. So we might as well punt and make it even easier with the mouse. thanks to this nifty open source plug-in for Eclipse. Once you’ve installed the plug-in, simply right-click on a project and select “Open in Terminal” to open a new xterm. Someone should contribute “Open in Finder” to that project, too.

Most keyboard shortcuts on Windows map directly to the map in the standard way; that is, you press the Command (Apple) key on the Mac instead of Ctrl on the PC. One notable exception is Ctrl+Space, which is identical on the Mac. Command+Space would be more consistent; however, this brings up the Spotlight search tool on the Mac. Since I’d rather have Ctrl+Space work in Eclipse where my muscle memory is expecting it, I swapped these keystrokes in Eclipse Preferences and Mac System Preferences. Another alternative is to disable Spotlight in favor of Google Quick Search Box.

Eclipse on Mac tricks welcome in the comments. No flames, please.

Posted in Eclipse | 7 Comments »

 
%d bloggers like this: