Sunday, January 23, 2011

Background progress with Channel API


For some time now, Google App Engine has made the Channel API available for everyone. The first thing that came to my mind was having a real time progress bar which would no longer be "refreshed" but triggered. Just like this one:

In other words, the server sends a message to the client to let it know something happened ! This the purpose of the Channel API.

Why a progress bar ? Well I tend to queue a lot tasks on App Engine. This is a great tool which helps doing things asynchronously and in a way, faster ! But the only way for me to know when a job is over is to actually wait for the tasks to run all. A bit frustrating... I must admit I tend to go to the App Engine task queues dashboard and press the refresh button to see numbers changing...

The goal of that post is to show how easy it was to make such a progress bar with GWT and the Channel API.

I'll assume you know a bit about GWT and GAE.

First of all you'll need a GWT libray which was made for this. Take time to read the HowToUse, everything is well explained.

Once you've added this library you only have a few lines of codes to write on the client side.
Two main steps:
  • Open a channel with a given token.
  • Wait for messages.
That's all !

This is the client code taken from my sample:

//token comes from server
Channel channel = ChannelFactory.createChannel(token);
channel.open(new SocketListener() {
    @Override
    public void onOpen() {
    	progress = 0;
    }
    @Override
    public void onMessage(String message) {
        JSONObject object = JSONParser.parseLenient(message)
                                      .isObject();
        double value = object.get(Keys.PROGRESS)
                             .isNumber()
                             .doubleValue();
        updateProgress(value);
    }
    public void onClose() {
	//Well it's closed...
    }
    public void onError(int code, String message) {
        //Should warn user ?
    }
});

And here is what I did on the server:

try {
    JSONObject object = new JSONObject();
    object.put(Keys.PROGRESS, percentage_done);
    //channelId is the same than the one use to generate token
    ChannelMessage msg = new ChannelMessage(channelId, message);
    ChannelServiceFactory.getChannelService().sendMessage(msg);
}catch(ChannelFailureException e){
    logger.severe(e.getMessage());
    logger.severe("Failed to push message "+message);			
} catch (JSONException e) {
    logger.severe(e.getMessage());
}

That's quite hard to make it simpler, right ?!
First time I used the Channel API I got confused between generated token and channelId and tried to use the token when sending messages.
It is actually pretty simple to remember:
  • channelId is used on server.
  • token is for client side.
It's worthless expanding on how I did the progress bar itself...

So here is the code.


	
		.container{
			text-align: center;
		}
		.progressContainer{
			border: 1px solid #CCC;
			width: 300px; 
			margin: 2px auto; 
			padding: 1px; 
			background: white;
			-moz-border-radius: 5px;
			border-radius: 5px;
		}
		.progressBar{
			text-align: right;
			background-color: #ACE97C;
  			height: 15px;
  			-moz-border-radius: 5px;
			border-radius: 5px;
		}
	
	
		
		
			
				
			
		
		
 

The updateProgress() method then just update the width of the progressBar.

As you can see, this would have been as easy doing it in HTML + JavaScript. The purpose was to show that adding this kind of feature to GWT apps is so easy that it would be a shame not to do it.

That's all for now.