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

net.tascalate.concurrent.Promise Maven / Gradle / Ivy

Go to download

Implementation of blocking (IO-Bound) cancellable java.util.concurrent.CompletionStage and related extensions to java.util.concurrent.ExecutorService-s

There is a newer version: 0.9.8
Show newest version
/**
 * 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 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 supplier, long timeout, TimeUnit unit) { return onTimeout(supplier, timeout, unit, true); } default Promise onTimeout(Supplier supplier, long timeout, TimeUnit unit, boolean cancelOnTimeout) { return onTimeout(supplier, Timeouts.toDuration(timeout, unit), cancelOnTimeout); } default Promise onTimeout(Supplier supplier, Duration duration) { return onTimeout(supplier, duration, true); } default Promise onTimeout(Supplier 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, 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 fn); Promise thenApplyAsync(Function fn); Promise thenApplyAsync(Function fn, Executor executor); Promise thenAccept(Consumer action); Promise thenAcceptAsync(Consumer action); Promise thenAcceptAsync(Consumer action, Executor executor); Promise thenRun(Runnable action); Promise thenRunAsync(Runnable action); Promise thenRunAsync(Runnable action, Executor executor); Promise thenCombine(CompletionStage other, BiFunction fn); Promise thenCombineAsync(CompletionStage other, BiFunction fn); Promise thenCombineAsync(CompletionStage other, BiFunction fn, Executor executor); Promise thenAcceptBoth(CompletionStage other, BiConsumer action); Promise thenAcceptBothAsync(CompletionStage other, BiConsumer action); Promise thenAcceptBothAsync(CompletionStage other, BiConsumer 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 other, Function fn); Promise applyToEitherAsync(CompletionStage other, Function fn); Promise applyToEitherAsync(CompletionStage other, Function fn, Executor executor); Promise acceptEither(CompletionStage other, Consumer action); Promise acceptEitherAsync(CompletionStage other, Consumer action); Promise acceptEitherAsync(CompletionStage other, Consumer 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> fn); Promise thenComposeAsync(Function> fn); Promise thenComposeAsync(Function> 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 predicate) { return thenFilter(predicate, NO_SUCH_ELEMENT); } default Promise thenFilter(Predicate predicate, Function errorSupplier) { return thenCompose(v -> predicate.test(v) ? this : failure(errorSupplier, v)); } default Promise thenFilterAsync(Predicate predicate) { return thenFilterAsync(predicate, NO_SUCH_ELEMENT); } default Promise thenFilterAsync(Predicate predicate, Function errorSupplier) { return thenComposeAsync(v -> predicate.test(v) ? this : failure(errorSupplier, v)); } default Promise thenFilterAsync(Predicate predicate, Executor executor) { return thenFilterAsync(predicate, NO_SUCH_ELEMENT, executor); } default Promise thenFilterAsync(Predicate predicate, Function errorSupplier, Executor executor) { return thenComposeAsync(v -> predicate.test(v) ? this : failure(errorSupplier, v), executor); } Promise whenComplete(BiConsumer action); Promise whenCompleteAsync(BiConsumer action); Promise whenCompleteAsync(BiConsumer action, Executor executor); Promise handle(BiFunction fn); Promise handleAsync(BiFunction fn); Promise handleAsync(BiFunction fn, Executor executor); }