Search code examples
javareturn-valuecompletable-futureautocloseablecloseable

Return autocloseable object inside a CompletableFuture and use it in whenComplete


I want to return an autocloseable object inside a CompletableFuture and use it in whenComplete without going to close it manually later.

This is the code that I've tried, but of course it won't work because it closes automatically when I return Jedis object. I haven't got any other ideas.

@Override
    public CompletableFuture<Jedis> execute() {
       if(!isServiceActive()) return CompletableFuture.completedFuture(null);
       return CompletableFuture.supplyAsync(() -> {
           try (Jedis jedis = pool.getResource()) {
              return jedis;
          } catch (RuntimeException e) {
              logger.severe("Impossibile ottenere una nuova risorsa Jedis!", e);
              throw new CompletionException(e);
          }
      }, executor);
  }

Solution

  • In general, this is not possible; CompletableFuture is not designed for this. As you've noted, you can't close the resource before completing the future, because then it will be closed before any consumers get to act on it. That means the consumer will have to be responsible for closing the resource.

    However, if your goal is to only need that Jedis instance in the one whenComplete call, then perhaps there is an alternative solution. Either modify execute() or create a new method that looks something like the following:

    <T> CompletableFuture<T> execute(Function<? super Jedis, ? extends T> func) {
        if (!isServiceActive()) return CompletableFuture.completedFuture(null);
        return CompletableFuture.supplyAsync(() -> {
            try (Jedis jedis = pool.getResource()) {
                return func.apply(jedis);
            } catch (Exception ex) {
                // log exception
                throw new CompletionException(ex);
            }
        }), executor);
    }
    

    The Jedis instance is closed by the time the future completes, but it still lets you do the needed work in the Function implementation. Using it would look like:

    execute(jedis -> {
        // use Jedis instance
        return result;
    })
    .whenComplete((result, error) -> {
        if (error != null) {
            // process error
        } else {
           // process result
        }
    });
    

    Though I don't know if your code can be modified to use this approach, as I notice your execute() method is an override (whether from a class/interface you control, I don't know). But even if you can't modify execute() directly or supply an overload feasibly, you might still be able to create a utility method that does something similar to the above.