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-2019 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.selectFirst;
import static net.tascalate.concurrent.SharedFunctions.unwrapExecutionException;
import static net.tascalate.concurrent.SharedFunctions.wrapCompletionException;

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.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 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) .raw() ); } CompletableFuture> delayed = new CompletableFuture<>(); whenComplete(Timeouts.configureDelay(this, delayed, duration, delayOnError)); // Use *Async to execute on default "this" executor return this.dependent() .thenApply(Try::success, false) .exceptionally(Try::failure, true) .thenCombineAsync(delayed, (u, v) -> u.done(), PromiseOrigin.ALL) .raw(); } 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); Promise result = this.dependent() .thenApply(Try::success, false) .exceptionally(Try::failure, true) // Use *Async to execute on default "this" executor .applyToEitherAsync(onTimeout, v -> Try.doneOrTimeout(v, duration), PromiseOrigin.ALL); result.whenComplete(Timeouts.timeoutsCleanup(this, onTimeout, cancelOnTimeout)); return result.raw(); } 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) { // timeout converted to supplier Promise> onTimeout = Timeouts.delayed(Try.success(value), duration); Promise result = this.dependent() .thenApply(Try::success, false) .exceptionally(Try::failure, true) // Use *Async to execute on default "this" executor .applyToEitherAsync(onTimeout, Try::done, PromiseOrigin.ALL); result.whenComplete(Timeouts.timeoutsCleanup(this, onTimeout, cancelOnTimeout)); return result.raw(); } 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.with(supplier), duration); Promise result = this.dependent() // resolved value converted to supplier of Try .thenApply(Try::success, false) .exceptionally(Try::failure, true) .thenApply(SharedFunctions::supply, true) // Use *Async to execute on default "this" executor .applyToEitherAsync(onTimeout, s -> s.get().done(), PromiseOrigin.ALL); result.whenComplete(Timeouts.timeoutsCleanup(this, onTimeout, cancelOnTimeout)); return result.raw(); } /** * 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 */ default DependentPromise dependent() { return DependentPromise.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 */ default DependentPromise dependent(Set defaultEnlistOptions) { return DependentPromise.from(this, defaultEnlistOptions); } 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} * @return * the underlying un-decorated {@link Promise} */ 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); 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); }