
net.tascalate.concurrent.Promise Maven / Gradle / Ivy
Show all versions of net.tascalate.concurrent Show documentation
/**
* Copyright 2015-2020 Valery Silaev (http://vsilaev.com)
*
* 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 net.tascalate.concurrent;
import static net.tascalate.concurrent.SharedFunctions.NO_SUCH_ELEMENT;
import static net.tascalate.concurrent.SharedFunctions.unwrapExecutionException;
import static net.tascalate.concurrent.SharedFunctions.wrapCompletionException;
import static net.tascalate.concurrent.SharedFunctions.failure;
import static net.tascalate.concurrent.SharedFunctions.selectFirst;
import java.time.Duration;
import java.util.Set;
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.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import net.tascalate.concurrent.decorators.ExecutorBoundPromise;
/**
* {@link Promise} is a combination of the {@link CompletionStage} and {@link Future} contracts.
* It provides both composition methods of the former and blocking access methods of the later.
*
Every composition method derived from the {@link CompletionStage} interface is overridden to
* return a new Promise;
* @author vsilaev
*
* @param
* a type of the successfully resolved promise value
*/
public interface Promise extends Future, CompletionStage {
default T getNow(T valueIfAbsent) throws CancellationException, CompletionException {
return getNow(SharedFunctions.supply(valueIfAbsent));
}
default T getNow(Supplier extends T> valueIfAbsent) throws CancellationException, CompletionException {
if (isDone()) {
try {
return get();
} catch (InterruptedException ex) {
// Should not happen when isDone() returns true
throw new RuntimeException(ex);
} catch (ExecutionException ex) {
throw wrapCompletionException(unwrapExecutionException(ex));
}
} else {
return valueIfAbsent.get();
}
}
default T join() throws CancellationException, CompletionException {
try {
return get();
} catch (InterruptedException ex) {
throw new CompletionException(ex);
} catch (ExecutionException ex) {
throw wrapCompletionException(unwrapExecutionException(ex));
}
}
default boolean isCompletedExceptionally() {
if (!isDone()) {
return false;
}
if (isCancelled()) {
return true;
} else {
try {
// We are done, so no blocking
get();
} catch (Throwable ex) {
return true;
}
return false;
}
}
// @Decorator
default Promise onCancel(Runnable code) {
return new ExtraCancellationPromise<>(this, code).postConstruct();
}
default Promise delay(long timeout, TimeUnit unit) {
return delay(timeout, unit, true);
}
default Promise delay(long timeout, TimeUnit unit, boolean delayOnError) {
return delay(Timeouts.toDuration(timeout, unit), delayOnError);
}
default Promise delay(Duration duration) {
return delay(duration, true);
}
default Promise delay(Duration duration, boolean delayOnError) {
if (!delayOnError) {
// Fast route
return thenCompose(v ->
this.dependent()
.thenCombineAsync(Timeouts.delay(duration), selectFirst(), PromiseOrigin.PARAM_ONLY)
.unwrap()
);
}
CompletableFuture> delayed = new CompletableFuture<>();
whenComplete(Timeouts.configureDelay(this, delayed, duration, delayOnError));
return this.dependent()
.handle(Try.liftResult(), false)
// Use *Async to execute on default "this" executor
.thenCombineAsync(delayed, selectFirst(), PromiseOrigin.ALL)
.thenCompose(Try::asPromise, true)
.unwrap();
}
default Promise orTimeout(long timeout, TimeUnit unit) {
return orTimeout(timeout, unit, true);
}
default Promise orTimeout(long timeout, TimeUnit unit, boolean cancelOnTimeout) {
return orTimeout(Timeouts.toDuration(timeout, unit), cancelOnTimeout);
}
default Promise orTimeout(Duration duration) {
return orTimeout(duration, true);
}
default Promise orTimeout(Duration duration, boolean cancelOnTimeout) {
Promise> onTimeout = Timeouts.delayed(null, duration);
DependentPromise result =
this.dependent()
.handle(Try.liftResult(), false)
// Use *Async to execute on default "this" executor
.applyToEitherAsync(onTimeout, v -> Try.doneOrTimeout(v, duration), PromiseOrigin.ALL)
.thenCompose(Try::asPromise, true);
result.whenComplete(Timeouts.timeoutsCleanup(this, onTimeout, cancelOnTimeout));
return result.unwrap();
}
default Promise onTimeout(T value, long timeout, TimeUnit unit) {
return onTimeout(value, timeout, unit, true);
}
default Promise onTimeout(T value, long timeout, TimeUnit unit, boolean cancelOnTimeout) {
return onTimeout(value, Timeouts.toDuration(timeout, unit), cancelOnTimeout);
}
default Promise onTimeout(T value, Duration duration) {
return onTimeout(value, duration, true);
}
default Promise onTimeout(T value, Duration duration, boolean cancelOnTimeout) {
Promise> onTimeout = Timeouts.delayed(Try.success(value), duration);
DependentPromise result =
this.dependent()
.handle(Try.liftResult(), false)
// Use *Async to execute on default "this" executor
.applyToEitherAsync(onTimeout, Function.identity(), PromiseOrigin.ALL)
.thenCompose(Try::asPromise, true);
result.whenComplete(Timeouts.timeoutsCleanup(this, onTimeout, cancelOnTimeout));
return result.unwrap();
}
default Promise onTimeout(Supplier extends T> supplier, long timeout, TimeUnit unit) {
return onTimeout(supplier, timeout, unit, true);
}
default Promise onTimeout(Supplier extends T> supplier, long timeout, TimeUnit unit, boolean cancelOnTimeout) {
return onTimeout(supplier, Timeouts.toDuration(timeout, unit), cancelOnTimeout);
}
default Promise onTimeout(Supplier extends T> supplier, Duration duration) {
return onTimeout(supplier, duration, true);
}
default Promise onTimeout(Supplier extends T> supplier, Duration duration, boolean cancelOnTimeout) {
// timeout converted to supplier
Promise>> onTimeout = Timeouts.delayed(Try.call(supplier), duration);
DependentPromise result =
this.dependent()
.handle(Try.liftResult(), false)
.thenApply(SharedFunctions::supply, true)
// Use *Async to execute on default "this" executor
.applyToEitherAsync(onTimeout, Supplier::get, PromiseOrigin.ALL)
.thenCompose(Try::asPromise, true);
result.whenComplete(Timeouts.timeoutsCleanup(this, onTimeout, cancelOnTimeout));
return result.unwrap();
}
/**
* Converts this {@link Promise} to a {@link DependentPromise}
* The returned DependentPromise does not implicitly enlist any {@link CompletionStage}
* for cancellation (neither self, nor passed as arguments to combining methods);
* only enlisting via explicit parameter is supported
*
* @return
* created DependentPromise
*/
// @Decorator
default DependentPromise dependent() {
return ConfigurableDependentPromise.from(this);
}
/**
* Converts this {@link Promise} to a {@link DependentPromise}
* The returned DependentPromise does implicitly enlist {@link CompletionStage}
* for cancellation (either self, and/or passed as arguments to combining methods)
* according to defaultEnlistOptions
parameter
*
* @param defaultEnlistOptions
* defines what {@link CompletionStage} should be enlisted implicitly for cancellation
* @return
* created DependentPromise
*/
// @Decorator
default DependentPromise dependent(Set defaultEnlistOptions) {
return ConfigurableDependentPromise.from(this, defaultEnlistOptions);
}
// @Decorator
default Promise defaultAsyncOn(Executor executor) {
return new ExecutorBoundPromise<>(this, executor);
}
/**
* Decorate this {@link Promise} with a decorator specified
* @param
* type of the actual promise decorator
* @param decoratorFactory
* a factory to create a concrete decorator
* @return
* a decorator created
*/
default D as(Function super Promise, D> decoratorFactory) {
return decoratorFactory.apply(this);
}
/**
* Unwraps underlying {@link Promise} if it was decorated (removes one level of decorators)
* @return
* the underlying un-decorated {@link Promise} or self if not decorated
*/
default Promise unwrap() {
return this;
}
/**
* Fully unwraps underlying {@link Promise} (removes all decoration layers)
* @return
* the underlying un-decorated {@link Promise} or self if not decorated
*/
default Promise raw() {
return this;
}
Promise thenApply(Function super T, ? extends U> fn);
Promise thenApplyAsync(Function super T, ? extends U> fn);
Promise thenApplyAsync(Function super T, ? extends U> fn, Executor executor);
Promise thenAccept(Consumer super T> action);
Promise thenAcceptAsync(Consumer super T> action);
Promise thenAcceptAsync(Consumer super T> action, Executor executor);
Promise thenRun(Runnable action);
Promise thenRunAsync(Runnable action);
Promise thenRunAsync(Runnable action, Executor executor);
Promise thenCombine(CompletionStage extends U> other, BiFunction super T, ? super U, ? extends V> fn);
Promise thenCombineAsync(CompletionStage extends U> other, BiFunction super T, ? super U, ? extends V> fn);
Promise thenCombineAsync(CompletionStage extends U> other,
BiFunction super T, ? super U, ? extends V> fn,
Executor executor);
Promise thenAcceptBoth(CompletionStage extends U> other, BiConsumer super T, ? super U> action);
Promise thenAcceptBothAsync(CompletionStage extends U> other, BiConsumer super T, ? super U> action);
Promise thenAcceptBothAsync(CompletionStage extends U> other,
BiConsumer super T, ? super U> action,
Executor executor);
Promise runAfterBoth(CompletionStage> other, Runnable action);
Promise runAfterBothAsync(CompletionStage> other, Runnable action);
Promise runAfterBothAsync(CompletionStage> other,
Runnable action,
Executor executor);
Promise applyToEither(CompletionStage extends T> other, Function super T, U> fn);
Promise applyToEitherAsync(CompletionStage extends T> other, Function super T, U> fn);
Promise applyToEitherAsync(CompletionStage extends T> other,
Function super T, U> fn,
Executor executor);
Promise acceptEither(CompletionStage extends T> other, Consumer super T> action);
Promise acceptEitherAsync(CompletionStage extends T> other, Consumer super T> action);
Promise acceptEitherAsync(CompletionStage extends T> other,
Consumer super T> action,
Executor executor);
Promise runAfterEither(CompletionStage> other, Runnable action);
Promise runAfterEitherAsync(CompletionStage> other, Runnable action);
Promise runAfterEitherAsync(CompletionStage> other,
Runnable action,
Executor executor);
Promise thenCompose(Function super T, ? extends CompletionStage> fn);
Promise thenComposeAsync(Function super T, ? extends CompletionStage> fn);
Promise thenComposeAsync(Function super T, ? extends CompletionStage> fn, Executor executor);
Promise exceptionally(Function fn);
default Promise exceptionallyAsync(Function fn) {
return PromiseHelper.exceptionallyAsync(this, fn);
}
default Promise exceptionallyAsync(Function fn, Executor executor) {
return PromiseHelper.exceptionallyAsync(this, fn, executor);
}
default Promise exceptionallyCompose(Function> fn) {
return PromiseHelper.exceptionallyCompose(this, fn);
}
default Promise exceptionallyComposeAsync(Function> fn) {
return PromiseHelper.exceptionallyComposeAsync(this, fn);
}
default Promise exceptionallyComposeAsync(Function> fn, Executor executor) {
return PromiseHelper.exceptionallyComposeAsync(this, fn, executor);
}
default Promise thenFilter(Predicate super T> predicate) {
return thenFilter(predicate, NO_SUCH_ELEMENT);
}
default Promise thenFilter(Predicate super T> predicate, Function super T, Throwable> errorSupplier) {
return thenCompose(v -> predicate.test(v) ? this : failure(errorSupplier, v));
}
default Promise thenFilterAsync(Predicate super T> predicate) {
return thenFilterAsync(predicate, NO_SUCH_ELEMENT);
}
default Promise thenFilterAsync(Predicate super T> predicate, Function super T, Throwable> errorSupplier) {
return thenComposeAsync(v -> predicate.test(v) ? this : failure(errorSupplier, v));
}
default Promise thenFilterAsync(Predicate super T> predicate, Executor executor) {
return thenFilterAsync(predicate, NO_SUCH_ELEMENT, executor);
}
default Promise thenFilterAsync(Predicate super T> predicate, Function super T, Throwable> errorSupplier, Executor executor) {
return thenComposeAsync(v -> predicate.test(v) ? this : failure(errorSupplier, v), executor);
}
Promise whenComplete(BiConsumer super T, ? super Throwable> action);
Promise whenCompleteAsync(BiConsumer super T, ? super Throwable> action);
Promise whenCompleteAsync(BiConsumer super T, ? super Throwable> action, Executor executor);
Promise handle(BiFunction super T, Throwable, ? extends U> fn);
Promise handleAsync(BiFunction super T, Throwable, ? extends U> fn);
Promise handleAsync(BiFunction super T, Throwable, ? extends U> fn, Executor executor);
}