javaslang.concurrent.Promise Maven / Gradle / Ivy
/* / \____ _ _ ____ ______ / \ ____ __ _______
* / / \/ \ / \/ \ / /\__\/ // \/ \ // /\__\ JΛVΛSLΛNG
* _/ / /\ \ \/ / /\ \\__\\ \ // /\ \ /\\/ \ /__\ \ Copyright 2014-2016 Javaslang, http://javaslang.io
* /___/\_/ \_/\____/\_/ \_/\__\/__/\__\_/ \_// \__/\_____/ Licensed under the Apache License, Version 2.0
*/
package javaslang.concurrent;
import javaslang.control.Try;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ForkJoinPool;
import static javaslang.concurrent.Future.DEFAULT_EXECUTOR_SERVICE;
/**
* A Promise is a write-once wrapper around a read-only Future which can complete the underlying Future with a value
* or an exception.
*
* The underlying {@code ExecutorService} is used to execute asynchronous handlers, e.g. via
* {@code promise.future().onComplete(...)}.
*
*
Creation
*
* Promise offers static factory methods to create new promises which hasn't been fulfilled yet:
*
* - create new promises: {@link Promise#make()}
*
* And we may create new promises that are already finished:
*
* - {@link #failed(Throwable)}
* - {@link #fromTry(Try)}
* - {@link #successful(Object)}
*
* All the static factory methods mentioned above have additional versions which take an {@link ExecutorService} as
* argument. This gives us more control over thread creation and thread pool sizes.
*
* One-shot API
*
* The main purpose of a {@code Promise} is to complete its underlying {@code Future}. When only a single {@code Thread}
* will eventually complete the {@code Promise}, we use one of these methods. Calls will throw if the {@code Promise} is already
* completed.
*
* - {@link #complete(Try)}
* - {@link #completeWith(Future)}
* - {@link #failure(Throwable)}
* - {@link #success(Object)}
*
*
* API for competing threads
*
* When multiple {@code Thread}s may complete our {@code Promise}, we typically use one of these methods. Calls will
* gracefully return {@code false} if the {@code Promise} is already completed.
*
* - {@link #tryComplete(Try)}
* - {@link #tryCompleteWith(Future)}
* - {@link #tryFailure(Throwable)}
* - {@link #trySuccess(Object)}
*
*
* @param The result type of the underlying {@code Future}.
* @author Daniel Dietrich
* @since 2.0.0
*/
public interface Promise {
/**
* Creates a failed {@code Promise}, backed by the {@link Future#DEFAULT_EXECUTOR_SERVICE}.
*
* @param exception The reason why it failed.
* @param The value type of a successful result.
* @return A failed {@code Promise}.
* @throws NullPointerException if exception is null
*/
static Promise failed(Throwable exception) {
Objects.requireNonNull(exception, "exception is null");
return failed(DEFAULT_EXECUTOR_SERVICE, exception);
}
/**
* Creates a failed {@code Promise}, backed by the given {@link ExecutorService}.
*
* @param executorService An {@code ExecutorService} passed to the underlying {@link Future}.
* @param exception The reason why it failed.
* @param The value type of a successful result.
* @return A failed {@code Promise}.
* @throws NullPointerException if executorService or exception is null
*/
static Promise failed(ExecutorService executorService, Throwable exception) {
Objects.requireNonNull(executorService, "executorService is null");
Objects.requireNonNull(exception, "exception is null");
return Promise. make(executorService).failure(exception);
}
/**
* Creates a {@code Promise} from a {@link Try}, backed by the {@link Future#DEFAULT_EXECUTOR_SERVICE}.
*
* @param result The result.
* @param The value type of a successful result.
* @return A completed {@code Promise} which contains either a {@code Success} or a {@code Failure}.
* @throws NullPointerException if result is null
*/
static Promise fromTry(Try extends T> result) {
return fromTry(DEFAULT_EXECUTOR_SERVICE, result);
}
/**
* Creates a {@code Promise} from a {@link Try}, backed by the given {@link ExecutorService}.
*
* @param executorService An {@code ExecutorService} passed to the underlying {@link Future}.
* @param result The result.
* @param The value type of a successful result.
* @return A completed {@code Promise} which contains either a {@code Success} or a {@code Failure}.
* @throws NullPointerException if executorService or result is null
*/
static Promise fromTry(ExecutorService executorService, Try extends T> result) {
Objects.requireNonNull(executorService, "executorService is null");
Objects.requireNonNull(result, "result is null");
return Promise. make(executorService).complete(result);
}
/**
* Makes a {@code Promise} that isn't fulfilled yet, backed by the {@link Future#DEFAULT_EXECUTOR_SERVICE}.
* {@link ForkJoinPool#commonPool()}.
*
* @param Result type of the {@code Promise}.
* @return A new {@code Promise}.
*/
static Promise make() {
return make(DEFAULT_EXECUTOR_SERVICE);
}
/**
* Makes a {@code Promise} that isn't fulfilled yet, backed by the given {@link ExecutorService}.
*
* @param executorService An {@code ExecutorService} passed to the underlying {@link Future}.
* @param Result type of the {@code Promise}.
* @return A new {@code Promise}.
* @throws NullPointerException if executorService is null
*/
static Promise make(ExecutorService executorService) {
Objects.requireNonNull(executorService, "executorService is null");
return new PromiseImpl<>(new FutureImpl<>(executorService));
}
/**
* Narrows a widened {@code Promise extends T>} to {@code Promise}
* by performing a type safe-cast. This is eligible because immutable/read-only
* collections are covariant.
*
* @param promise A {@code Promise}.
* @param Component type of the {@code Promise}.
* @return the given {@code promise} instance as narrowed type {@code Promise}.
*/
@SuppressWarnings("unchecked")
static Promise narrow(Promise extends T> promise) {
return (Promise) promise;
}
/**
* Creates a succeeded {@code Promise}, backed by the {@link Future#DEFAULT_EXECUTOR_SERVICE}.
*
* @param result The result.
* @param The value type of a successful result.
* @return A succeeded {@code Promise}.
*/
static Promise successful(T result) {
return successful(DEFAULT_EXECUTOR_SERVICE, result);
}
/**
* Creates a succeeded {@code Promise}, backed by the given {@link ExecutorService}.
*
* @param executorService An {@code ExecutorService} passed to the underlying {@link Future}.
* @param result The result.
* @param The value type of a successful result.
* @return A succeeded {@code Promise}.
* @throws NullPointerException if executorService is null
*/
static Promise successful(ExecutorService executorService, T result) {
Objects.requireNonNull(executorService, "executorService is null");
return Promise. make(executorService).success(result);
}
/**
* Returns the {@link ExecutorService} used by this {@code Future}.
*
* @return The underlying {@code ExecutorService}.
*/
ExecutorService executorService();
/**
* Returns the underlying {@link Future} of this {@code Promise}.
*
* @return The {@code Future}.
*/
Future future();
/**
* Checks if this {@code Promise} is completed, i.e. has a value.
*
* @return true, if the computation successfully finished or failed, false otherwise.
*/
default boolean isCompleted() {
return future().isCompleted();
}
/**
* Completes this {@code Promise} with the given {@code value}.
*
* @param value Either a {@link Try.Success} containing the result or a {@link Try.Failure} containing an exception.
* @return This {@code Promise}.
* @throws IllegalStateException if this {@code Promise} has already been completed.
*/
default Promise complete(Try extends T> value) {
if (tryComplete(value)) {
return this;
} else {
throw new IllegalStateException("Promise already completed.");
}
}
/**
* Attempts to completes this {@code Promise} with the given {@code value}.
*
* @param value Either a {@link Try.Success} containing the result or a {@link Try.Failure} containing an exception.
* @return {@code false} if this {@code Promise} has already been completed, {@code true} otherwise.
* @throws IllegalStateException if this {@code Promise} has already been completed.
*/
boolean tryComplete(Try extends T> value);
/**
* Completes this {@code Promise} with the given {@code Future}, once that {@code Future} is completed.
*
* @param other Another {@code Future} to react on.
* @return This {@code Promise}.
*/
default Promise completeWith(Future extends T> other) {
return tryCompleteWith(other);
}
/**
* Attempts to complete this {@code Promise} with the specified {@code Future}, once that {@code Future} is completed.
*
* @param other Another {@code Future} to react on.
* @return This {@code Promise}.
*/
default Promise tryCompleteWith(Future extends T> other) {
other.onComplete(this::tryComplete);
return this;
}
/**
* Completes this {@code Promise} with the given {@code value}.
*
* @param value A value.
* @return This {@code Promise}.
* @throws IllegalStateException if this {@code Promise} has already been completed.
*/
default Promise success(T value) {
return complete(Try.success(value));
}
/**
* Completes this {@code Promise} with the given {@code value}.
*
* @param value A value.
* @return {@code false} if this {@code Promise} has already been completed, {@code true} otherwise.
*/
default boolean trySuccess(T value) {
return tryComplete(Try.success(value));
}
/**
* Completes this {@code Promise} with the given {@code exception}.
*
* @param exception An exception.
* @return This {@code Promise}.
* @throws IllegalStateException if this {@code Promise} has already been completed.
*/
default Promise failure(Throwable exception) {
return complete(Try.failure(exception));
}
/**
* Completes this {@code Promise} with the given {@code exception}.
*
* @param exception An exception.
* @return {@code false} if this {@code Promise} has already been completed, {@code true} otherwise.
*/
default boolean tryFailure(Throwable exception) {
return tryComplete(Try.failure(exception));
}
}
/**
* Internal {@code Promise} implementation.
*
* @param result type
* @author Daniel Dietrich
* @since 2.0.0
*/
final class PromiseImpl implements Promise {
final FutureImpl future;
PromiseImpl(FutureImpl future) {
this.future = future;
}
@Override
public ExecutorService executorService() {
return future.executorService();
}
@Override
public Future future() {
return future;
}
@Override
public boolean tryComplete(Try extends T> value) {
return future.tryComplete(value);
}
// The underlying FutureImpl is MUTABLE and therefore we CANNOT CHANGE DEFAULT equals() and hashCode() behavior.
// See http://stackoverflow.com/questions/4718009/mutable-objects-and-hashcode
@Override
public String toString() {
return "Promise(" + future.getValue().map(String::valueOf).getOrElse("?") + ")";
}
}