Friday, February 20, 2009

GWT Tips 1 - Chain of Responsibility

GWT application development (and AJAX programming in general) can sometimes prove problematic for web developers who has not yet adopted the mindset of asynchronous programming. In GWT, you make a RPC as follows:


foo.bar(param, new AsycnCallback<object>) {
public void onFailure(Throwable throwable) {
// handle exception
}
public void onSuccess(Object result) {
// successful execution of asynchronous RPC
}
}


What if you have to make a series of RPC calls such that an RPC call only gets invoked after the previous call has succeeded. Of course we can simply wrap this series of RPC calls inside one single RPC on the server side so that the client does not have to worry about synchronising the calls, but you maybe you want to update the user interface between calls, or you may want to execute some client-side logic before making the second call.

You may start off writing code like this:


foo.bar(param, new AsycnCallback<object>) {
public void onFailure(Throwable throwable) {
...
}
public void onSuccess(Object result) {
// some client-side logic before invoking the second RPC
foo2.bar2(param, new AsycnCallback<object>) {
public void onFailure(Throwable throwable) {
....
}
public void onSuccess(Object object) {
....
}
}
}
}


This is hard to follow and error-prone. A way to avoid nested RPC calls is to leverage the Chain of Responsibility design pattern. The main class in the design pattern is the AbstractHandler. The following is a basic implementation of this class:


/**
* An abstract handler in the Chain or Responsibility design pattern.
*
* @author See Wah Cheng
* @param <T>
* type of request object to handle
*/
public abstract class AbstractHandler<T> {

private Handler<T> successor;

public void setSuccessor(Handler<T> successor) {
this.successor = successor;
}

public Handler<T> getSuccessor() {
return successor;
}

public void handOverToSuccessor(T request) {
if (successor != null) {
successor.handleRequest(request);
}
}

public abstract void handleRequest(T request);
}


Given this class, to execute a series of RPC calls, one simply has to create a concrete implementation of this AbstractHandler for each RPC call and chain them together as follows: (where InvocationContext is some kind of class encapsulating the details needed for the various RPC invocation)


public class FooInvoker extends AbstractHandler<Invocationcontext> {
public void handleRequest(InvocationContext request) {
Object param = request.getFooParam();
foo.bar(param, new AsycnCallback<object>) {
public void onFailure(Throwable throwable) { // handle exception }
public void onSuccess(Object result) {
// some client-side logic before invoking the next RPC
// updating request object if necessary
handOverToSuccessor(request);
}
}
}
}

public class Foo2Invoker extends AbstractHandler<InvocationContext> {
public void handleRequest(InvocationContext request) {
Object param = request.getFoo2Param();
foo2.bar2(param, new AsycnCallback<object>) {
public void onFailure(Throwable throwable) { // handle exception }
public void onSuccess(Object result) {
// some client-side logic before invoking the next RPC
// updating request object if necessary
handOverToSuccessor(request);
}
}
}
}

InvocationContext request;
...
FooInvoker fooInvoker = new FooInvoker();
Foo2Invoker foo2Invoker = new Foo2Invoker();
fooInvoker.setSuccessor(foo2Invoker);
fooInvoker.handleRequest(request);


OK you may be shouting, "Isn't this a lot of code for something pretty simple? Er, agile? Hello?" Yes, it is more verbose, and whether this approach offers any benefit depends on the situation. If you are making three or more RPC calls in series, I definitely think this will lead to better quality code. If, inside each callback's onSuccess method, you are doing more than just invoking the next RPC, this will certainly lead to better code. If the individual stages are needed elsewhere, this again will lead to more reusable code.

Finally, an added benefit of this is that the order of execution can be altered quite easy just by shuffling the handler order in the chain (assuming that the order of execution does not matter!)

6 comments:

cometta said...

u specified dummyAuthenticationProvider

but there is no authenticationManager specified in the xml?

do we need to include this?


can u share your tutorial sample source code?

See Wah Cheng said...

Hi I suppose you are referring to the previous blog entry?

http://seewah.blogspot.com/2009/02/gwt-and-spring-security.html

NO this single PROVIDER in the applicationContext will get picked up automatically by Spring as the default PROVIDER, and no MANAGER is needed in this particular instance.

Sorry I have not had time to package up any sort of deployable source code, but I hope you have been able to do some testing based with the snippets of code I have posted in the blog!

suresh said...

what is Handler here...
im pretty new to this design pattern.. but i need this...
i cant able to understand the code...
can you post some detailed code..

See Wah Cheng said...

try this http://www.javaworld.com/javaworld/jw-08-2003/jw-0829-designpatterns.html for a detailed but easy to understand introduction to this design pattern.

Essentially, you refactored each stage of your multi-stage process into its own "handler".

Have you tried running the code in the last codebox in the post? If you are confused as to what InvocationContext does, just create an object like this:

public class InvocationContext {
List<Object> list = new ArrayList<Object>();
public ArrayList<Object> getFooParam() {
return list;
}
}

and then add to and remove from the list inside the foo.bar and the foo2.bar2 rpc calls to see how these handlers can service the request.

nicola said...

Good article See, actually I'm making some attempts on implementing this and I found that it could be useful to retrieve the result from a node when it has finished processing, and stop there the chain.
You too said that a good thing of this pattern is the encapsulation of the code of a single RPC call in a class so it can be used more times: one time I will use it in a chain, another I should think to use it on its own. What do you think? regards

Anonymous said...

If it describes more about the InvocationContext, it would be a perfect example. At the beginning, I thought the InvocationContext was the built-in feature of GWT. Anyway, it is inspiring.