javajava-8future

Transform Java Future into a CompletableFuture


Java 8 introduces CompletableFuture, a new implementation of Future that is composable (includes a bunch of thenXxx methods). I'd like to use this exclusively, but many of the libraries I want to use return only non-composable Future instances.

Is there a way to wrap up a returned Future instances inside of a CompleteableFuture so that I can compose it?


Solution

  • There is a way, but you won't like it. The following method transforms a Future<T> into a CompletableFuture<T>:

    public static <T> CompletableFuture<T> makeCompletableFuture(Future<T> future) {
      if (future.isDone())
        return transformDoneFuture(future);
      return CompletableFuture.supplyAsync(() -> {
        try {
          if (!future.isDone())
            awaitFutureIsDoneInForkJoinPool(future);
          return future.get();
        } catch (ExecutionException e) {
          throw new RuntimeException(e);
        } catch (InterruptedException e) {
          // Normally, this should never happen inside ForkJoinPool
          Thread.currentThread().interrupt();
          // Add the following statement if the future doesn't have side effects
          // future.cancel(true);
          throw new RuntimeException(e);
        }
      });
    }
    
    private static <T> CompletableFuture<T> transformDoneFuture(Future<T> future) {
      CompletableFuture<T> cf = new CompletableFuture<>();
      T result;
      try {
        result = future.get();
      } catch (Throwable ex) {
        cf.completeExceptionally(ex);
        return cf;
      }
      cf.complete(result);
      return cf;
    }
    
    private static void awaitFutureIsDoneInForkJoinPool(Future<?> future)
        throws InterruptedException {
      ForkJoinPool.managedBlock(new ForkJoinPool.ManagedBlocker() {
        @Override public boolean block() throws InterruptedException {
          try {
            future.get();
          } catch (ExecutionException e) {
            throw new RuntimeException(e);
          }
          return true;
        }
        @Override public boolean isReleasable() {
          return future.isDone();
        }
      });
    }
    

    Obviously, the problem with this approach is, that for each Future, a thread will be blocked to wait for the result of the Future--contradicting the idea of futures. In some cases, it might be possible to do better. However, in general, there is no solution without actively wait for the result of the Future.