Java / Scala Future driven by a callback

2.7k views Asked by At

Short Version:

How can I create a Promise<Result> which is completed on a trigger of a callback?

Long Version:

I am working on an application which deals with third-party SOAP services. A request from user delegates to multiple SOAP services simultaneously, aggregates the results and sends back to the user.

The system needs to be scalable and should allow multiple concurrent users. As each user requests ends up triggering about 10 web service calls and each call blocking for about 1 second, the system needs to be designed with non-blocking I/O.

I am using Apache CXF within Play Framework (Java) for this system. I have managed to generate the Asynchronous WS Client proxies and enable the async transport. What I am unable to figure out is how to return a Future to Play's Thread when I have delegated to multiple Web Service proxies and the results will be obtained as callbacks.

Option 1: Using async method calls returning Java Future.

As described in this scala.concurrent.Future wrapper for java.util.concurrent.Future thread, there is no way we can convert a Java Future to a Scala Future. Only way to get a result from the Future is to do Future.get() which blocks the caller. Since CXF's generated proxies return Java Future, this option is ruled out.

Option 2: Use Scala Future.

Since CXF generates the proxy interfaces, I am not sure if there is any way I can intervene and return a Scala Future (AFAIK Akka uses Scala Futures) instead of Java Future?

Option 3: Use the callback approach.

The async methods generated by CXF which return Java Future also takes a callback object which I suppose will provide a callback when result is ready. To use this approach, I will need to return a Future which will wait until I receive a callback.

I think Option 3 is most promising, although I have no ideas about how I can return a Promise which will be completed on receiving a callback. I could possibly have a thread waiting in a while(true) and waiting in between until result is available. Again, I don't know how I can go into wait without blocking the thread?

In a nutshell, I am trying to build a system which is making a lot of SOAP web service calls, where each call blocks for significant time. The system may easily run out of threads in case of lot of concurrent web service calls. I am working on finding a solution which is non-blocking I/O based which can allow many ongoing web service calls at the same time.

1

There are 1 answers

2
bjfletcher On BEST ANSWER

Option 3 looks good :) A couple of imports to start with...

import scala.concurrent.{Await, Promise}
import scala.concurrent.duration.Duration

and, just to illustrate the point, here's a mocked CXF API that takes the callback:

def fetch(url: String, callback: String => Unit) = {
  callback(s"results for $url")
}

Create a promise, call API with promise as callback:

val promise = Promise[String]
fetch("http://corp/api", result => promise.success(result))

Then you can take promise.future which is an instance of Future into your Play app.

To test it, you can do this:

Await.result(promise.future, Duration.Inf)

which will block awaiting the result, at which point you should see "results for http://corp/api" in the console.