31
votes

I am in the process of writing a small scala wrapper around a java library.

The java library has an object QueryExecutor exposing 2 methods:

  • execute(query): Result
  • asyncExecute(query): ListenableFuture[Result]

ListenableFuture in this context is the one from the guava library.

I want my scala wrapper to return a Future[Result] instead of the java object, but I am not sure what is the best way to implement that. Here are 2 solutions I came up with:

future {
  executor.execute(query)
}

and

val p = promise[Result]
val guavaFuture = executor.asyncExecute(query)

Futures.addCallback(guavaFuture, new FutureCallback[Result] {
  def onFailure(t: Throwable) {
    p.failure(t)
  }

  def onSuccess(result: Result) {
    p.success(result)
  }
})

p.future

I am wondering which method is the best. My intuition is that the first one, while returning a Future, will still block a thread while the call to execute waits for a response, the second one looks like it should be really non blocking. Any comment on the pros/cons of each method ?

2
Lets assume you have 4 processors. In this case default ExecutionContext consists of 4 workers. Each future { executor.execute(query) } blocks 1 worker, so 4 "futures" will block your program entirely. You could create additional ExecutionContext for blocking operations, but there would be some overhead.senia
Thanks @senia, that's what I thought. The first code is async from the caller's point of view but will still block a thread of the ExecutionContext, while the second one is really non blocking (assuming that asyncExecute uses non blocking IO). I feel like this is a very basic question but I am not very familiar with Promises.vptheron
I found this to be helpful in a similar (or possible even identical) requirment : github.com/eigengo/activator-akka-cassandra/blob/master/src/…Gavin

2 Answers

45
votes

The second option is best, it keeps everything asynchronous. but... you can do one better and abstract the solution into a reusable pattern:

implicit class RichListenableFuture[T](lf: ListenableFuture[T]) {
  def asScala: Future[T] = {
    val p = Promise[T]()
    Futures.addCallback(lf, new FutureCallback[T] {
      def onFailure(t: Throwable): Unit = p failure t
      def onSuccess(result: T): Unit    = p success result
    })
    p.future
  }    
}

You can then simply call:

executor.asyncExecute(query).asScala
3
votes

Another, slightly shorter solution:

implicit class ListenableFutureDecorator[T](val f: ListenableFuture[T]) extends AnyVal {
  def asScala(implicit e: Executor): Future[T] = {
    val p = Promise[T]()
    f.addListener(() => p.complete(Try(f.get())), e)
    p.future
  }
}