44

I'm looking for a Java pattern for making a nested sequence of non-blocking method calls. In my case, some client code needs to asynchronously invoke a service to perform some use case, and each step of that use case must itself be performed asynchronously (for reasons outside the scope of this question). Imagine I have existing interfaces as follows:

public interface Request {} 

public interface Response {} 

public interface Callback<R extends Response> {
    void onSuccess(R response);
    void onError(Exception e);
}

There are various paired implementations of the Request and Response interfaces, namely RequestA + ResponseA (given by the client), RequestB + ResponseB (used internally by the service), etc.

The processing flow looks like this:

Sequence diagram showing nested callbacks.

In between the receipt of each response and the sending of the next request, some additional processing needs to happen (e.g. based on values in any of the previous requests or responses).

So far I've tried two approaches to coding this in Java:

  • anonymous classes: gets ugly quickly because of the required nesting
  • inner classes: neater than the above, but still hard for another developer to comprehend the flow of execution

Is there some pattern to make this code more readable? For example, could I express the service method as a list of self-contained operations that are executed in sequence by some framework class that takes care of the nesting?

Andrew Swan
  • 13,427
  • 22
  • 69
  • 98
  • I know you said non-blocking was a necessity, but is there any way to reconsider that? Could you make a second thread that blocks on each request, for example? I could see that code being very clear with care. – Rob I May 22 '12 at 02:56
  • 1
    self-contained operations will still be in some form of classes, anonymous or not, but still physically present. will have to wait for the project lambda to have more natural constructs for things like you're doing. I would also assume you need all this within "plain java" paradigms? Doesn't sound like you would like an external orchestration framework – Pavel Veller May 22 '12 at 03:10
  • @Rob I: sadly, non-blocking is a must-have. – Andrew Swan May 22 '12 at 03:14
  • @Pavel Veller: "plain Java" is preferable, but if there's something out there that helps code this neatly, I'll consider it. Which part of Project Lambda (http://openjdk.java.net/projects/lambda/) did you see as being useful here? – Andrew Swan May 22 '12 at 03:18
  • 3
    @AndrewSwan then since the implementation (not only the interface) must not block, I like your list idea - set up a list of "operations" (perhaps `Future`s?), for which the setup should be pretty clear. Then upon receiving each response, the next operation should be invoked. With a little imagination, this sounds like the [chain of responsibility](http://en.wikipedia.org/wiki/Chain-of-responsibility_pattern). – Rob I May 22 '12 at 03:23
  • I've looked at commons-chain, but unfortunately it only seems to support synchronous execution, because it blocks between each execution of Command#execute(). I'll see what I can whip up from scratch. – Andrew Swan May 22 '12 at 07:52
  • @Rob I, if you could make your CoR idea an answer, I can upvote it. – Andrew Swan May 23 '12 at 23:35
  • 1
    Would the [RxJava](https://github.com/ReactiveX/RxJava/wiki/Getting-Started) help? – fifth Sep 02 '14 at 13:51
  • It might, from the quick look I just had. If you'd like to add that as an answer, I'll upvote it. – Andrew Swan Sep 03 '14 at 00:54
  • SO doesn't want me to answer my own question, so I'll just note here that Spring 5 seems to offer some assistance when writing fully async applications: https://spring.io/blog/2016/07/28/reactive-programming-with-spring-5-0-m1 – Andrew Swan Jul 29 '16 at 04:57
  • It's also not answering my own question, but Kotlin is adding coroutines to avoid "callback hell": https://github.com/Kotlin/kotlin-coroutines/blob/master/kotlin-coroutines-informal.md – Andrew Swan Aug 15 '16 at 04:11

4 Answers4

14

Since the implementation (not only the interface) must not block, I like your list idea.

Set up a list of "operations" (perhaps Futures?), for which the setup should be pretty clear and readable. Then upon receiving each response, the next operation should be invoked.

With a little imagination, this sounds like the chain of responsibility. Here's some pseudocode for what I'm imagining:

public void setup() {
    this.operations.add(new Operation(new RequestA(), new CallbackA()));
    this.operations.add(new Operation(new RequestB(), new CallbackB()));
    this.operations.add(new Operation(new RequestC(), new CallbackC()));
    this.operations.add(new Operation(new RequestD(), new CallbackD()));
    startNextOperation();
}
private void startNextOperation() {
    if ( this.operations.isEmpty() ) { reportAllOperationsComplete(); }
    Operation op = this.operations.remove(0);
    op.request.go( op.callback );
}
private class CallbackA implements Callback<Boolean> {
    public void onSuccess(Boolean response) {
        // store response? etc?
        startNextOperation();
    }
}
...
Rob I
  • 5,627
  • 2
  • 21
  • 28
  • 1
    This put me on the right track, so I've accepted it. It ended up being more like a linked list, with each operation having a reference to the following operation, as explained in this blog post: http://seewah.blogspot.sg/2009/02/gwt-tips-1-chain-of-responsibility.html (in my case the code was more complicated because there were multiple types of request and response). – Andrew Swan Jun 15 '12 at 00:58
7

In my opinion, the most natural way to model this kind of problem is with Future<V>.

So instead of using a callback, just return a "thunk": a Future<Response> that represents the response that will be available at some point in the future.

Then you can either model subsequent steps as things like Future<ResponseB> step2(Future<ResponseA>), or use ListenableFuture<V> from Guava. Then you can use Futures.transform() or one of its overloads to chain your functions in a natural way, but while still preserving the asynchronous nature.

If used in this way, Future<V> behaves like a monad (in fact, I think it may qualify as one, although I'm not sure off the top of my head), and so the whole process feels a bit like IO in Haskell as performed via the IO monad.

Daniel Pryden
  • 59,486
  • 16
  • 97
  • 135
  • One problem of j.u.c.Future is that Future.get() cannot be called from a task which executes under j.u.c.Executor (may cause a deadlock), only from a separate thread. – Alexei Kaigorodov May 23 '12 at 19:45
  • @AlexeiKaigorodov: I think it's safe as long as each task has its own `Executor` (which is basically what you said). That's probably the best reason to use `ListenableFuture`: to shield you from blocking on a call to `Future.get()`. – Daniel Pryden May 23 '12 at 22:07
2

You can use actor computing model. In your case, the client, services, and callbacks [B-D] all can be represented as actors.

There are many actor libraries for java. Most of them, however, are heavyweight, so I wrote a compact and extendable one: df4j. It considers actor model as a specific case of more general dataflow computing model and, as a result, allows user to create new types of actors, to optimally fit user's requirements.

Alexei Kaigorodov
  • 13,189
  • 1
  • 21
  • 38
  • You seem to be the author of df4j, in which case I think you ought to disclose that when recommending it. – Andrew Swan May 22 '12 at 08:45
  • Are there any examples of using df4j to solve the nested callback problem in particular? – Andrew Swan May 23 '12 at 02:42
  • 1
    I do not consider "nested callback problem" as a problem, since as soon as callbacks are represented as actors, then nesting disappear. df4j has many examples of using actors. Actors are objects which run asynchronously. References to actors can be send in messages, so that the actor which handles the message can access the referenced actor. The idea of df4j is that user can extend it to fit to the particular use case (say, make an actor-like class which implements Callback and another class with a method send(Request, Callback)). – Alexei Kaigorodov May 23 '12 at 19:38
  • Thanks for explaining that, I'm not so familiar with Actors. – Andrew Swan May 23 '12 at 23:34
  • I've since come across Akka, which looks like it could help in this scenario. – Andrew Swan Jan 14 '14 at 05:53
1

I am not sure if I get you question correctly. If you want to invoke a service and on its completion result need to be passed to other object which can continue processing using result. You can look at using Composite and Observer to achive this.

ejb_guy
  • 1,125
  • 6
  • 6