All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.spotify.futures.CompletableFuturesExtra Maven / Gradle / Ivy

There is a newer version: 4.3.3
Show newest version
/*
 * Copyright (c) 2013-2019 Spotify AB
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package com.spotify.futures;

import com.google.api.core.ApiFuture;
import com.google.common.util.concurrent.ListenableFuture;

import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Executor;
import java.util.function.BiFunction;
import java.util.function.Function;

public class CompletableFuturesExtra {

  private CompletableFuturesExtra() {
    throw new AssertionError();
  }

  /**
   * Wrap a {@link CompletableFuture} in a {@link ListenableFuture}. The returned future will
   * complete with the same result or failure as the original future.
   *
   * @param future The {@link CompletableFuture} to wrap in a {@link ListenableFuture}.
   * @return A {@link ListenableFuture} that completes when the original future completes.
   */
  public static  ListenableFuture toListenableFuture(
      CompletableFuture future) {
    return toListenableFuture((CompletionStage) future);
  }

  /**
   * Wrap a {@link CompletionStage} in a {@link ListenableFuture}. The returned future will
   * complete with the same result or failure as the original future.
   *
   * @param future The {@link CompletionStage} to wrap in a {@link ListenableFuture}.
   * @return A {@link ListenableFuture} that completes when the original future completes.
   */
  public static  ListenableFuture toListenableFuture(
      CompletionStage future) {
    if (future instanceof ListenableToCompletableFutureWrapper) {
      return ((ListenableToCompletableFutureWrapper) future).unwrap();
    }
    return new CompletableToListenableFutureWrapper<>(future);
  }

  /**
   * Wrap a {@link ListenableFuture} in a {@link CompletableFuture}. The returned future will
   * complete with the same result or failure as the original future. Completing the returned
   * future does not complete the original future.
   *
   * @deprecated - see {@link ListenableFuturesExtra#toCompletableFuture(ListenableFuture)}
   *
   * @param future The {@link ListenableFuture} to wrap in a {@link CompletableFuture}.
   * @return A {@link CompletableFuture} that completes when the original future completes.
   */
  @Deprecated
  public static  CompletableFuture toCompletableFuture(ListenableFuture future) {
    return ListenableFuturesExtra.toCompletableFuture(future);
  }

  /**
   * Converts an {@link ApiFuture} to a {@link CompletableFuture}.
   *
   * @deprecated - see {@link ApiFuturesExtra#toCompletableFuture(ApiFuture)}
   *
   * @param future the {@link ApiFuture} to wrap.
   * @return a {@link CompletableFuture} that completes when the original future completes.
   */
  @Deprecated
  public static  CompletableFuture toCompletableFuture(ApiFuture future) {
    return ApiFuturesExtra.toCompletableFuture(future);
  }

  /**
   * Converts an {@link ApiFuture} to a {@link CompletableFuture}.
   *
   * @deprecated - see {@link ApiFuturesExtra#toCompletableFuture(ApiFuture)}
   *
   * @param future the {@link ApiFuture} to wrap.
   * @param executor the executor where the listener is running.
   * @return a {@link CompletableFuture} that completes when the original future completes.
   */
  @Deprecated
  public static  CompletableFuture toCompletableFuture(ApiFuture future,
                                                             Executor executor) {
    return ApiFuturesExtra.toCompletableFuture(future, executor);
  }

  /**
   * Wrap a {@link CompletionStage} in a {@link ApiFuture}. The returned future will
   * complete with the same result or failure as the original future.
   *
   * @param future The {@link CompletionStage} to wrap in a {@link ApiFuture}.
   * @return A {@link ApiFuture} that completes when the original future completes.
   */
  public static  ApiFuture toApiFuture(CompletionStage future) {
    if (future instanceof ApiFutureToCompletableFutureWrapper) {
      return ((ApiFutureToCompletableFutureWrapper) future).unwrap();
    }
    return new CompletableToApiFutureWrapper<>(future);
  }

  /**
   * Returns a new CompletableFuture that is already exceptionally completed with
   * the given exception.
   *
   * @param throwable the exception
   * @return the exceptionally completed CompletableFuture
   */
  public static  CompletableFuture exceptionallyCompletedFuture(Throwable throwable) {
    final CompletableFuture future = new CompletableFuture<>();
    future.completeExceptionally(throwable);
    return future;
  }

  /**
   * Returns a new stage that, when this stage completes
   * either normally or exceptionally, is executed with this stage's
   * result and exception as arguments to the supplied function.
   *
   * 

When this stage is complete, the given function is invoked * with the result (or {@code null} if none) and the exception (or * {@code null} if none) of this stage as arguments, and the * function's result is used to complete the returned stage. * *

This differs from * {@link java.util.concurrent.CompletionStage#handle(java.util.function.BiFunction)} * in that the function should return a {@link java.util.concurrent.CompletionStage} rather than * the value directly. * * @param stage the {@link CompletionStage} to compose * @param fn the function to use to compute the value of the * returned {@link CompletionStage} * @param the function's return type * @return the new {@link CompletionStage} */ public static CompletionStage handleCompose( CompletionStage stage, BiFunction> fn) { return dereference(stage.handle(fn)); } /** * Returns a new stage that, when this stage completes * exceptionally, is executed with this stage's exception as the * argument to the supplied function. Otherwise, if this stage * completes normally, then the returned stage also completes * normally with the same value. * *

This differs from * {@link java.util.concurrent.CompletionStage#exceptionally(java.util.function.Function)} * in that the function should return a {@link java.util.concurrent.CompletionStage} rather than * the value directly. * * @param stage the {@link CompletionStage} to compose * @param fn the function to use to compute the value of the * returned {@link CompletionStage} if this stage completed * exceptionally * @return the new {@link CompletionStage} */ public static CompletionStage exceptionallyCompose( CompletionStage stage, Function> fn) { return dereference(wrap(stage).exceptionally(fn)); } /** * check that a stage is completed. * @param stage a {@link CompletionStage}. * @throws IllegalStateException if the stage is not completed. */ public static void checkCompleted(CompletionStage stage) { if (!stage.toCompletableFuture().isDone()) { throw new IllegalStateException("future was not completed"); } } /** * Get the value of a completed stage. * * @param stage a completed {@link CompletionStage}. * @return the value of the stage if it has one. * @throws IllegalStateException if the stage is not completed. * @throws java.util.concurrent.CompletionException if the future completed exceptionally. */ public static T getCompleted(CompletionStage stage) { CompletableFuture future = stage.toCompletableFuture(); checkCompleted(future); return future.join(); } /** * Get the exception from an exceptionally completed stage. * * @param stage a completed {@link CompletionStage}. * @return the exception of the stage if it has one. * @throws IllegalStateException if the stage is not completed, or not completed exceptionally. */ public static Throwable getCompletedException(CompletionStage stage) { CompletableFuture future = stage.toCompletableFuture(); checkCompleted(future); if (!future.isCompletedExceptionally()) { throw new IllegalStateException("future was not completed exceptionally"); } try { future.join(); throw new IllegalStateException("Unreachable"); } catch (CompletionException | CancellationException e) { return e; } } /** * This takes a stage of a stage of a value and * returns a plain stage of a value. * * @param stage a {@link CompletionStage} of a {@link CompletionStage} of a value * @return the {@link CompletionStage} of the value */ public static CompletionStage dereference( CompletionStage> stage) { //noinspection unchecked return stage.thenCompose(Identity.INSTANCE); } private static CompletionStage> wrap(CompletionStage future) { //noinspection unchecked return future.thenApply((Function>) WrapFunction.INSTANCE); } private enum Identity implements Function { INSTANCE; @Override public Object apply(Object o) { return o; } } private enum WrapFunction implements Function { INSTANCE; @Override public Object apply(Object o) { return CompletableFuture.completedFuture(o); } } }