13

I need to find a way to convert from Future to ListenableFuture. Currently i'm using a service which returns Future but i need to hook up a listener to it. I can't change the service interface as it doesn't belong to me.

Is there a simple way to do that ?

I have read guava docs but still i can't find a way to do it.

qwwdfsad
  • 3,077
  • 17
  • 25
justatester
  • 391
  • 3
  • 11
  • Where does your `Future` come from? Is it from an `ExecutorService`? – Will May 11 '15 at 03:50
  • I would be surprised if there were a good way to do this without creating your own thread; `ListenableFuture` is strictly more powerful than `Future`. – Louis Wasserman May 11 '15 at 05:29
  • @Will my Future comes from https://cloud.google.com/appengine/docs/java/javadoc/com/google/appengine/api/urlfetch/URLFetchService#fetchAsync(com.google.appengine.api.urlfetch.HTTPRequest). I couldn't find a better way to fetch urls asynchronously from gae/j. – justatester May 11 '15 at 16:07

2 Answers2

24

Guava provides the JdkFutureAdapters types for this conversion. The API states

Utilities necessary for working with libraries that supply plain Future instances.

For example

Future<?> future = ...;
ListenableFuture<?> listenable = JdkFutureAdapters.listenInPoolThread(future);

But you should use it with care: it's hard to emulate listenable future when you already have submitted task, because there is no way to put a hook on completion there, so Guava takes a new thread and blocks there until original Future is completed.

The Guava Wiki also contains some information on this specific case.

danw
  • 1,528
  • 1
  • 17
  • 17
qwwdfsad
  • 3,077
  • 17
  • 25
0

Future is just get Interface while Guava ListenableFuture is Future interface with registered Runnable listener(s) run by complete() when set or setException (implemented by guava AbstractFuture).

import com.google.common.util.concurrent.AbstractFuture;
import java.util.concurrent.Future;

public class ListenerFuture<V> extends AbstractFuture<V> {

    public ListenerFuture(Future<V> future){
        this.future= future;
    }
    // blocking in future get, then run listener in AbstractFuture set
    public void fireListener(){
        try {
            super.set(future.get());
        }catch (Exception e){
            throw new RuntimeException("guava set ListenableFuture", e);
        }
    }

    private Future<V> future;
}

ListenerFuture<V> response= new ListenerFuture(service.response());
response.addListener(Runnable, Executor);
// pass the ListenableFuture to whom need it
// do something else until who must have service response call the blocking
response.fileListner()

Guava AbstractFuture has its limitations:

  1. Listener is lists, but usually only 1 used - overkill. If multiple listeners are needed, fork it inside your listener or think about your design using messaging.
  2. setException set return value as Exception, so user has to use instanceof to differentiate Exception or not at get()
  3. In Future pipeline, too many layers addListener() make code hard to read.

I prefer CompletableFuture.supply().thenApply().thenAccept().handle()

simple
  • 1
  • 1
  • 1
    The question states that "I can't change the service interface as it doesn't belong to me". So he has no control over how the Future was created. Your answer doesn't answer what the question asks. – Iogui Aug 27 '21 at 17:05
  • Please provide additional details in your answer. As it's currently written, it's hard to understand your solution. – Community Aug 27 '21 at 20:44