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

io.atlassian.util.concurrent.Promises Maven / Gradle / Ivy

Go to download

This project contains utility classes that are used by various products and projects inside Atlassian and may have some utility to the world at large.

There is a newer version: 4.0.1
Show newest version
/**
 * Copyright 2015 Atlassian Pty Ltd
 *
 * 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 io.atlassian.util.concurrent;

import javax.annotation.Nonnull;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

public final class Promises {

  private Promises() {}

  /**
   * Create a promise for the input effect.
   *
   * @param  type of the successful value.
   * @return a new {@link SettablePromise} that can be fulfilled by calling the
   * {@link Callback} methods it implements. Cancelling this Promise will have
   * no consequence on the code that calls the callback.
   */
  public static  SettablePromise settablePromise() {
    return settablePromise(Optional.empty());
  }

  /**
   * @param  type of the successful value.
   * @param executor to be called to run callbacks and transformations attached
   * to the returned Promise. If None, they will be executed on the caller
   * thread.
   * @return a new {@link SettablePromise} that can be fulfilled by calling the
   * {@link Callback} methods it implements. Cancelling this Promise will have
   * no consequence on the code that calls the callback.
   */
  public static  SettablePromise settablePromise(@Nonnull final Optional executor) {
    Objects.requireNonNull(executor, "Executor");
    return new Settable<>(executor);
  }

  /**
   * Create a promise from the input completion stage.
   *
   * @param stage a {@link java.util.concurrent.CompletionStage} used as the
   * precedent of the returned Promise.
   * @param  type of the successful value.
   * @return a new {@link io.atlassian.util.concurrent.Promise} that will be
   * fulfilled with the same result that the CompletionStage has. If it
   * implements
   * {@link java.util.concurrent.CompletionStage#toCompletableFuture()} then
   * cancelling this Promise will cancel that CompletableFuture.
   */
  public static  Promise forCompletionStage(@Nonnull final CompletionStage stage) {
    return forCompletionStage(stage, Optional.empty());
  }

  /**
   * Create a promise from the input completion stage.
   *
   * @param stage a {@link java.util.concurrent.CompletionStage} used as the
   * precedent of the returned Promise.
   * @param  type of the successful value.
   * @param executor to be called to run callbacks and transformations attached
   * to the returned Promise. If None, they will be executed on the caller
   * thread.
   * @return a new {@link io.atlassian.util.concurrent.Promise} that will be
   * fulfilled with the same result that the CompletionStage has. If it
   * implements
   * {@link java.util.concurrent.CompletionStage#toCompletableFuture()} then
   * cancelling this Promise will cancel that CompletableFuture.
   */
  public static  Promise forCompletionStage(@Nonnull final CompletionStage stage, @Nonnull final Optional executor) {
    Objects.requireNonNull(stage, "CompletionStage");
    Objects.requireNonNull(executor, "Executor");
    return new OfStage<>(stage, executor);
  }

  /**
   * Return a completable future based on the input promise
   *
   * @param promise a {@link io.atlassian.util.concurrent.Promise} used as the
   * precedent of the returned CompletableFuture.
   * @param  any super type of A that may be inferred.
   * @return a new {@link java.util.concurrent.CompletableFuture} that will be
   * fulfilled with the same result that the Promise has.
   */
  public static  CompletableFuture toCompletableFuture(@Nonnull final Promise promise) {
    if (promise instanceof OfStage) {
      // shortcut
      return ((OfStage) promise).future;
    } else {
      final CompletableFuture aCompletableFuture = new CompletableFuture<>();
      promise.then(compose(aCompletableFuture::complete, t -> {
        if (promise.isCancelled() && (!(t instanceof CancellationException))) {
          aCompletableFuture.completeExceptionally(new CancellationException(t.getMessage()));
        } else {
          aCompletableFuture.completeExceptionally(getRealException(t));
        }
      }));
      return aCompletableFuture;
    }
  }

  /**
   * Returns a new {@link io.atlassian.util.concurrent.Promise} representing the
   * status of a list of other promises.
   *
   * @param promises The promises that the new promise should track
   * @return The new, aggregate promise
   * @param  an A.
   */
  public @SafeVarargs static  Promise> when(@Nonnull final Promise... promises) {
    return when(Stream.of(promises));
  }

  /**
   * Returns a new {@link io.atlassian.util.concurrent.Promise} representing the
   * status of a list of other promises. More generally this is known as
   * {code}sequence{code} as both List and Promise are traversable monads.
   *
   * @param promises The promises that the new promise should track
   * @return The new, aggregate promise
   * @param  an A.
   */
  public static  Promise> when(@Nonnull final Iterable> promises) {
    return when(StreamSupport.stream(promises.spliterator(), false).map(Function.identity()));
  }

  /**
   * Returns a new {@link io.atlassian.util.concurrent.Promise} representing the
   * status of a stream of other promises.
   *
   * @param promises The promises that the new promise should track
   * @return The new, aggregate promise
   * @param  an A.
   */
  public static  Promise> when(@Nonnull final Stream> promises) {
    final List> futures = promises.map(Promises::toCompletableFuture).collect(Collectors.toList());

    final CompletableFuture allFutures = CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]));
    // short-circuit: eagerly cancel the futures if any of the leaves fail. Do
    // not wait for the others.
    futures.forEach(cf -> cf.whenComplete((a, t) -> {
      if (t != null)
        futures.forEach(f -> f.cancel(true));
    }));
    final Function> gatherValues = o -> futures.stream().map(CompletableFuture::join).collect(Collectors.toList());
    return Promises.forCompletionStage(allFutures.thenApply(gatherValues));
  }

  /**
   * Creates a new, resolved promise for the specified concrete value.
   *
   * @return The new promise
   * @since 2.7
   *
   * @deprecated use {@link Promises#promise(Object)} as a method reference.
   */
  public @Deprecated static  Function> toPromise() {
    return Promises::promise;
  }

  /**
   * Creates a new, resolved promise for the specified concrete value.
   *
   * @param value The value for which a promise should be created
   * @return The new promise
   * @param  an A.
   */
  public static  Promise promise(final A value) {
    final CompletableFuture future = new CompletableFuture<>();
    future.complete(value);
    return Promises.forCompletionStage(future);
  }

  /**
   * Creates a new, rejected promise from the given {@link java.lang.Throwable}
   * and result type.
   *
   * @param t The throwable
   * @return The new promise
   * @param  an A.
   */
  public static  Promise rejected(@Nonnull final Throwable t) {
    final CompletableFuture future = new CompletableFuture<>();
    future.completeExceptionally(t);
    return Promises.forCompletionStage(future);
  }

  /**
   * Creates a promise from the given future.
   *
   * @param future The future delegate for the new promise
   * @param executor an executor where a task will run that waits for the future
   * to complete
   * @param  possible future value type
   * @return The new promise
   */
  public static  Promise forFuture(@Nonnull final Future future, @Nonnull final Executor executor) {
    final CompletableFuture newFuture = new CompletableFuture<>();
    executor.execute(() -> {
      try {
        newFuture.complete(future.get());
      } catch (final ExecutionException ee) {
        newFuture.completeExceptionally(ee.getCause());
      } catch (final InterruptedException ee) {
        newFuture.cancel(true);
      } catch (final Throwable t) {
        newFuture.completeExceptionally(t);
      }
    });
    newFuture.whenComplete((a, t) -> {
      if (t instanceof CancellationException) {
        future.cancel(true);
      }
    });
    return forCompletionStage(newFuture, Optional.of(executor));
  }

  /**
   * Create a {@link Promise.TryConsumer} by composing two {@link Consumer}.
   *
   * @param success To run if the Future is successful
   * @param failure To run if the Future fails
   * @return The composed Callback
   * @param  an A.
   */
  public static  Promise.TryConsumer compose(@Nonnull final Consumer success, @Nonnull final Consumer failure) {
    return new Promise.TryConsumer() {
      public void accept(final A result) {
        success.accept(result);
      }

      public void fail(@Nonnull final Throwable t) {
        failure.accept(t);
      }
    };
  }

  static class OfStage implements Promise {

    private final CompletableFuture future;
    private final Optional executor;

    public OfStage(@Nonnull final CompletionStage delegate, @Nonnull final Optional ex) {
      future = buildCompletableFuture(delegate, ex);
      executor = ex;
    }

    @Override public A claim() {
      try {
        return future.get();
      } catch (InterruptedException e) {
        throw new RuntimeInterruptedException(e);
      } catch (CompletionException e) {
        final Throwable cause = e.getCause();
        if (cause instanceof RuntimeException) {
          throw (RuntimeException) cause;
        }
        if (cause instanceof Error) {
          throw (Error) cause;
        }
        throw e;
      } catch (ExecutionException e) {
        final Throwable cause = e.getCause();
        if (cause instanceof RuntimeException) {
          throw (RuntimeException) cause;
        }
        if (cause instanceof Error) {
          throw (Error) cause;
        }
        throw new RuntimeException(cause);
      }
    }

    @Override public Promise done(final Consumer e) {
      return then(e, t -> {});
    }

    @Override public Promise fail(final Consumer e) {
      return then(a -> {}, e);
    }

    @Override public Promise then(final TryConsumer callback) {
      return then(callback::accept, callback::fail);
    }

    @Override public  Promise map(final Function function) {
      return forCompletionStage(future.thenApply(function));
    }

    @Override public  Promise flatMap(final Function> f) {
      final Function> fn = a -> Promises.toCompletableFuture(f.apply(a));
      return this.>, B> newPromise(future::thenCompose, future::thenComposeAsync).apply(fn);
    }

    @Override public Promise recover(final Function handleThrowable) {
      return forCompletionStage(future.exceptionally(handleThrowable.compose(Promises::getRealException)));
    }

    @Override public  Promise fold(final Function ft, final Function fa) {
      final Function fn = a -> {
        try {
          return fa.apply(a);
        } catch (final Throwable t) {
          return ft.apply(t);
        }
      };
      return this., B> newPromise(future::handle, future::handleAsync).apply(biFunction(fn, ft));
    }

    @Override public boolean cancel(final boolean mayInterruptIfRunning) {
      return future.cancel(mayInterruptIfRunning);
    }

    @Override public boolean isCancelled() {
      return future.isCancelled();
    }

    @Override public boolean isDone() {
      return future.isDone();
    }

    @Override public A get() throws InterruptedException, ExecutionException {
      return future.get();
    }

    @Override public A get(final long timeout, @Nonnull final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
      return future.get(timeout, unit);
    }

    private Promise then(final Consumer onSuccess, final Consumer onFailure) {
      return this.newPromise(future::whenComplete, future::whenCompleteAsync).apply(biConsumer(onSuccess, onFailure));
    }

    private  Function> newPromise(final Function> f1, final BiFunction> f2) {
      return i -> {
        if (executor.isPresent()) {
          return forCompletionStage(f2.apply(i, executor.get()));
        } else {
          return forCompletionStage(f1.apply(i));
        }
      };
    }

    private CompletableFuture buildCompletableFuture(final CompletionStage completionStage, final Optional executor) {
      try {
        return completionStage.toCompletableFuture();
      } catch (final UnsupportedOperationException uoe) {
        final CompletableFuture aCompletableFuture = new CompletableFuture<>();

        BiConsumer action = biConsumer(aCompletableFuture::complete, aCompletableFuture::completeExceptionally);
        if (executor.isPresent()) {
          completionStage.whenCompleteAsync(action, executor.get());
        } else {
          completionStage.whenComplete(action);
        }
        return aCompletableFuture;
      }
    }
  }

  static class Settable extends OfStage implements SettablePromise {
    private final CompletableFuture completableFuture;

    public Settable(@Nonnull final Optional ex) {
      this(new CompletableFuture<>(), ex);
    }

    private Settable(@Nonnull final CompletableFuture cf, @Nonnull final Optional ex) {
      super(cf, ex);
      completableFuture = cf;
    }

    public void set(final A result) {
      completableFuture.complete(result);
    }

    public void exception(@Nonnull final Throwable t) {
      completableFuture.completeExceptionally(t);
    }
  }

  private static Throwable getRealException(@Nonnull final Throwable t) {
    if (t instanceof CompletionException) {
      return t.getCause();
    }
    return t;
  }

  private static  BiFunction biFunction(final Function f, final Function ft) {
    return (a, t) -> {
      if (t == null) {
        return f.apply(a);
      } else {
        return ft.apply(getRealException(t));
      }
    };
  }

  private static  BiConsumer biConsumer(final Consumer c, final Consumer ct) {
    return (a, t) -> {
      if (t == null) {
        c.accept(a);
      } else {
        ct.accept(getRealException(t));
      }
    };
  }

  /**
   * A callback that can be completed with a successful value or a failed
   * exception.
   * 
   * @param  type of the successful value.
   */
  public interface Callback {
    void set(A result);

    void exception(@Nonnull Throwable t);
  }

  /**
   * A promise that can be completed with a successful value or a failed
   * exception.
   * 
   * @param  type of the successful value.
   */
  public interface SettablePromise extends Promise, Callback {}

}