Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.vlingo.common.completes.FutureCompletes Maven / Gradle / Ivy
Go to download
These are just a few common tools shared across various vlingo projects.
// Copyright © 2012-2020 VLINGO LABS. All rights reserved.
//
// This Source Code Form is subject to the terms of the
// Mozilla Public License, v. 2.0. If a copy of the MPL
// was not distributed with this file, You can obtain
// one at https://mozilla.org/MPL/2.0/.
package io.vlingo.common.completes;
import io.vlingo.common.Cancellable;
import io.vlingo.common.Completes;
import io.vlingo.common.Scheduled;
import io.vlingo.common.Scheduler;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Function;
public class FutureCompletes implements Completes {
private static final long NoTimeout = -1L;
private final State state;
public FutureCompletes(final CompletesId id, final Scheduler scheduler) {
this.state = new State<>(id, scheduler, OutcomeType.Some);
}
public FutureCompletes(final Scheduler scheduler) {
this(Completes.completesId(), scheduler);
}
public FutureCompletes(final CompletesId id, final T outcome, final boolean successful) {
this(id, (Scheduler) null);
if (!successful) {
useFailedOutcomeOf(outcome);
}
with(outcome);
}
public FutureCompletes(final T outcome, final boolean successful) {
this(Completes.completesId(), outcome, successful);
}
public FutureCompletes(final CompletesId id, final T outcome) {
this(id, outcome, true);
}
public FutureCompletes(final T outcome) {
this(Completes.completesId(), outcome);
}
public FutureCompletes(final CompletesId id) {
this(id, (Scheduler) null);
}
public FutureCompletes() {
this(Completes.completesId());
}
@Override
public Completes andThen(final long timeout, final O failedOutcomeValue, final Function function) {
return new FutureCompletes<>(state.nextForFunction(failedOutcomeValue, function)).timeoutWithin(timeout);
}
@Override
public Completes andThen(final O failedOutcomeValue, final Function function) {
return andThen(NoTimeout, failedOutcomeValue, function);
}
@Override
public Completes andThen(final long timeout, final Function function) {
return andThen(timeout, null, function);
}
@Override
public Completes andThen(final Function function) {
return andThen(NoTimeout, null, function);
}
@Override
public Completes andThenConsume(final long timeout, final T failedOutcomeValue, final Consumer consumer) {
return new FutureCompletes<>(state.nextForConsumer(failedOutcomeValue, consumer)).timeoutWithin(timeout);
}
@Override
public Completes andThenConsume(final T failedOutcomeValue, final Consumer consumer) {
return andThenConsume(NoTimeout, failedOutcomeValue, consumer);
}
@Override
public Completes andThenConsume(final long timeout, final Consumer consumer) {
return andThenConsume(timeout, null, consumer);
}
@Override
public Completes andThenConsume(final Consumer consumer) {
return andThenConsume(NoTimeout, null, consumer);
}
@Override
@SuppressWarnings("unchecked")
public O andThenTo(final long timeout, final F failedOutcomeValue, final Function function) {
return (O) new FutureCompletes<>(state.nextForFunctionAsync((O) failedOutcomeValue, function)).timeoutWithin(timeout);
}
@Override
public O andThenTo(final F failedOutcomeValue, final Function function) {
return andThenTo(NoTimeout, failedOutcomeValue, function);
}
@Override
public O andThenTo(final long timeout, final Function function) {
return andThenTo(timeout, null, function);
}
@Override
public O andThenTo(final Function function) {
return andThenTo(NoTimeout, null, function);
}
@Override
@SuppressWarnings({"unchecked", "rawtypes"})
public Completes otherwise(final Function function) {
return new FutureCompletes<>(state.nextForFunction(null, (Function) function, true));
}
@Override
public Completes otherwiseConsume(final Consumer consumer) {
return new FutureCompletes<>(state.nextForConsumer(null, consumer, true));
}
@Override
public Completes recoverFrom(final Function function) {
return new FutureCompletes<>(state.nextForExceptional(function));
}
@Override
@SuppressWarnings("unchecked")
public Completes andFinally() {
// no-op
return (Completes) this;
}
@Override
public Completes andFinally(final Function function) {
return new FutureCompletes<>(state.nextForFunction(null, function));
}
@Override
public void andFinallyConsume(final Consumer consumer) {
new FutureCompletes<>(state.nextForConsumer(null, consumer));
}
@Override
public O await() {
return state.await();
}
@Override
public O await(final long timeout) {
return state.await(timeout);
}
@Override
public boolean isCompleted() {
return state.isCompleted();
}
@Override
public boolean hasFailed() {
return state.hasFailed();
}
@Override
public void failed() {
with(state.failureValue());
}
@Override
public void failed(final Exception exception) {
state.exceptional(exception);
}
@Override
public CompletesId id() {
return state.id();
}
@Override
public boolean hasOutcome() {
return state.hasOutcome();
}
@Override
public T outcome() {
return state.outcome();
}
@Override
public Completes repeat() {
state.repeat();
return this;
}
@Override
public Completes timeoutWithin(final long timeout) {
state.startTimer(timeout);
return this;
}
@Override
@SuppressWarnings("unchecked")
public Completes useFailedOutcomeOf(final F failedOutcomeValue) {
state.registerFailureOutcomeValue((T) failedOutcomeValue);
return this;
}
@Override
@SuppressWarnings("unchecked")
public Completes with(final O outcome) {
state.resetAll();
state.complete(outcome);
return (Completes) this;
}
@Override
public String toString() {
return "FutureCompletes [id=" + id() + ", next=" + (state.next != null ? state.next.id() : "(none)") + " state=" + state + "]";
}
private FutureCompletes(final State state) {
this.state = state;
}
private CompletableFuture asCompletableFuture() {
return state.future();
}
//////////////////////////////////////////////////////
// State
//////////////////////////////////////////////////////
private static class State implements Scheduled {
private Cancellable cancellable;
private State next;
private final State previous;
private final Function, CompletableFuture> futureFactory;
private final AtomicReference> future;
private final AtomicBoolean failed;
private final AtomicReference failureValue;
private final boolean handlesFailure;
private final CompletesId id;
private final AtomicReference> outcome;
private final OutcomeType outcomeType;
private final Scheduler scheduler;
private final AtomicBoolean timedOut = new AtomicBoolean(false);
private final AtomicBoolean repeats = new AtomicBoolean(false);
State(final CompletesId id, final State previous, final Scheduler scheduler, final Function, CompletableFuture> futureFactory, final T failedOutcomeValue, final boolean handlesFailure, final OutcomeType outcomeType) {
this.id = id;
this.previous = previous;
this.scheduler = scheduler;
this.failed = new AtomicBoolean(false);
this.failureValue = new AtomicReference<>(failedOutcomeValue);
this.handlesFailure = handlesFailure;
this.outcome = new AtomicReference<>(UncompletedOutcome.instance());
this.outcomeType = outcomeType;
this.futureFactory = futureFactory;
this.future = new AtomicReference<>(this.futureFactory.apply(this));
if (this.previous != null) {
this.previous.next = this;
}
}
State(final CompletesId id, final Scheduler scheduler, final OutcomeType outcomeType) {
this(id, null, scheduler, (state) -> new CompletableFuture<>(), null, false, outcomeType);
}
@SuppressWarnings("unchecked")
O await() {
if (isCompleted()) {
return (O) outcome();
}
try {
future().get();
return (O) outcome();
} catch (Exception e) {
if (this.hasFailed()) {
return (O) outcome();
}
// fall through
}
return null;
}
@SuppressWarnings("unchecked")
O await(final long timeout) {
if (isCompleted()) {
return (O) outcome();
}
try {
future().get(timeout, TimeUnit.MILLISECONDS);
return (O) outcome();
} catch (Exception e) {
if (this.hasFailed()) {
return (O) outcome();
}
// fall through
}
return null;
}
@SuppressWarnings("unchecked")
void complete(final O outcome) {
if (outcome instanceof Throwable) {
exceptional((Throwable) outcome);
return;
}
T realOutcome = (T) outcome;
if (isFailureValue(realOutcome)) {
realOutcome = failureValue();
fail(realOutcome, isTimedOut());
}
if (!hasOutcome()) {
outcome(new CompletedOutcome<>(realOutcome));
}
if (!future().isDone()) {
future().complete(realOutcome);
}
}
void repeat() {
this.repeats.set(true);
if (hasPrevious()) {
previous().repeat();
}
}
void resetAll() {
if (isCompleted() && hasRepeats()) {
first().resetAllFollowing();
}
}
void exceptional(final Throwable t) {
future().completeExceptionally(t);
}
T outcome() {
if (isCompleted()) {
return ultimateOutcome();
}
return null;
}
void outcome(final Outcome outcome) {
this.outcome.set(outcome);
}
boolean hasOutcome() {
return outcome.get().value() != null;
}
boolean isCompleted() {
return outcome.get().isCompleted() || future().isDone();
}
boolean hasFailed() {
if (failed.get()) {
return true;
}
final boolean hasFailed = future().isCancelled() || future().isCompletedExceptionally();
failed.set(hasFailed);
return hasFailed;
}
T failureValue() {
return failureValue.get();
}
boolean isFailureValue(final T candidateFailureValue) {
if (isTimedOut()) {
return true;
}
final T currentFailureValue = failureValue.get();
if (currentFailureValue == candidateFailureValue) return true;
return currentFailureValue != null && currentFailureValue.equals(candidateFailureValue);
}
void registerFailureOutcomeValue(final T failedOutcomeValue) {
final T currentFailureValue = failureValue.get();
if (currentFailureValue == failedOutcomeValue) return;
if (currentFailureValue != null && failedOutcomeValue == null) return;
if (currentFailureValue != null && currentFailureValue.equals(failedOutcomeValue)) return;
failureValue.set(failedOutcomeValue);
}
void startTimer(final long timeout) {
if (timeout > 0 && scheduler != null && cancellable == null) {
// 2L delayBefore prevents timeout until after return from here
cancellable = scheduler.scheduleOnce(this, null, 0L, timeout);
}
}
void cancelTimer() {
if (cancellable != null) {
cancellable.cancel();
cancellable = null;
}
}
void timedOut() {
failAllFollowing(failureValue(), true);
}
boolean isTimedOut() {
return timedOut.get();
}
@Override
public void intervalSignal(final Scheduled scheduled, final Object data) {
cancelTimer();
if (future().isDone()) return;
timedOut();
}
@Override
public String toString() {
return "State [id=" + id
+ ", outcome=" + (outcome == null ? "(none)" : outcome.get())
+ ", failed=" + failed.get()
+ ", handlesFailure=" + handlesFailure
+ ", timedOut=" + timedOut.get() + "]";
}
@SuppressWarnings({"unchecked", "rawtypes"})
State nextForConsumer(final T failedOutcomeValue, final Consumer consumer, final boolean handlesFailure) {
Function, CompletableFuture> factory = (State state) -> state.previousFuture().thenAccept(state.consumerWrapper(consumer));
return new State(Completes.completesId(), this, scheduler, factory, failedOutcomeValue, handlesFailure, OutcomeType.None);
}
State nextForConsumer(final T failedOutcomeValue, final Consumer consumer) {
return nextForConsumer(failedOutcomeValue, consumer, false);
}
@SuppressWarnings({"unchecked", "rawtypes"})
State nextForExceptional(final Function function) {
Function, CompletableFuture> factory = (State state) -> state.previousFuture().exceptionally(state.functionExceptionWrapper(function));
return new State(Completes.completesId(), this, scheduler, factory, null, false, OutcomeType.Some);
}
@SuppressWarnings({"unchecked", "rawtypes"})
State nextForFunction(final O failedOutcomeValue, final Function function, final boolean handlesFailure) {
Function, CompletableFuture> factory = (State state) -> state.previousFuture().thenApply(state.functionWrapper(function));
return new State(Completes.completesId(), this, scheduler, factory, failedOutcomeValue, handlesFailure, OutcomeType.Some);
}
State nextForFunction(final O failedOutcomeValue, final Function function) {
return nextForFunction(failedOutcomeValue, function, false);
}
@SuppressWarnings({"unchecked", "rawtypes"})
State nextForFunctionAsync(final O failedOutcomeValue, final Function function) {
Function, CompletableFuture> factory = (State state) -> state.previousFuture().thenComposeAsync(state.composableFunction(state.functionWrapper(function)));
return new State(Completes.completesId(), this, scheduler, factory, failedOutcomeValue, false, OutcomeType.Some);
}
@SuppressWarnings("unchecked")
private Function> composableFunction(final Function userFunction) {
return (T value) -> {
O outcome = userFunction.apply(value);
if (outcome instanceof FutureCompletes) {
return ((FutureCompletes) outcome).asCompletableFuture();
}
return CompletableFuture.completedFuture(outcome);
};
}
private Consumer consumerWrapper(final Consumer userConsumer) {
return (value) -> {
try {
// the current `future` is unsafe to reference here since it might not be initialised yet
if (previous.outcomeType == OutcomeType.None) {
value = previous.outcome();
}
if (handlesFailure && !previous.hasFailed()) {
return;
}
if (previous.hasFailed()) {
fail(previous.failureValue(), previous.isTimedOut());
if (!handlesFailure) {
return;
}
} else if (isFailureValue(value)) {
fail(failureValue(), isTimedOut());
if (!handlesFailure) {
return;
}
}
userConsumer.accept(value);
} catch (Throwable cause) {
fail(failureValue(), isTimedOut());
throw cause;
}
};
}
private Function functionExceptionWrapper(final Function userFunction) {
return (Throwable e) -> {
// the current `future` is unsafe to reference here since it might not be initialised yet
timedOut.set(previous.isTimedOut());
failed.set(true);
return userFunction.apply(unwrap(e));
};
}
@SuppressWarnings("unchecked")
private Function functionWrapper(final Function userFunction) {
return (value) -> {
try {
// the current `future` is unsafe to reference here since it might not be initialised yet
if (previous.outcomeType == OutcomeType.None) {
value = previous.outcome();
}
if (handlesFailure && !previous.hasFailed()) {
return (O) value;
}
if (previous.hasFailed()) {
fail(previous.failureValue(), previous.isTimedOut());
if (!handlesFailure) {
return (O) previous.failureValue();
}
} else if (isFailureValue(value)) {
fail(failureValue(), isTimedOut());
if (!handlesFailure) {
return (O) failureValue();
}
}
return userFunction.apply(value);
} catch (Exception cause) {
fail(failureValue(), isTimedOut());
throw cause;
}
};
}
private void resetAllFollowing() {
this.outcome.set(UncompletedOutcome.instance());
this.timedOut.set(false);
this.failed.set(false);
this.future(futureFactory.apply(this));
if (hasNext()) {
next().resetAllFollowing();
}
}
private void failAllFollowing(final T failureValue, final boolean hasTimedOut) {
// could overwrite possible otherwise(v -> f(v))
// computed outcome unless use short circuit here
if (handlesFailure) return;
fail(failureValue, hasTimedOut);
if (hasNext()) {
next().failAllFollowing(failureValue, hasTimedOut);
}
}
private void fail(final T failureValue, final boolean hasTimedOut) {
this.failureValue.set(failureValue);
this.timedOut.set(hasTimedOut);
this.failed.set(true);
if (!handlesFailure) {
outcome(new CompletedOutcome<>(failureValue));
}
}
private T ultimateOutcome() {
if (hasNext()) {
final State nextState = next();
if (nextState.isCompleted()) {
return nextState.ultimateOutcome();
}
}
final Outcome maybeOutcome = this.outcome.get();
if (maybeOutcome.isCompleted()) {
return maybeOutcome.value();
}
if (outcomeType == OutcomeType.None) {
Outcome outcome = previousOutcome();
if (null != outcome && outcome.isCompleted()) {
outcome(outcome);
return outcome.value();
}
}
return currentOutcome().value();
}
private Outcome previousOutcome() {
if (hasPrevious()) {
if (previous().outcomeType == OutcomeType.Some) {
return previous().currentOutcome();
}
return previous().previousOutcome();
}
return null;
}
private Outcome currentOutcome() {
final Outcome maybeOutcome = this.outcome.get();
if (maybeOutcome.isCompleted()) {
return maybeOutcome;
}
try {
if (future().isDone()) {
outcome(new CompletedOutcome<>(future().get()));
}
} catch (Exception e) {
// fall through
}
return this.outcome.get();
}
private boolean hasRepeats() {
return this.repeats.get();
}
private boolean hasPrevious() {
return this.previous != null;
}
private State previous() {
return this.previous;
}
private State> first() {
State> first = this;
while (first.previous != null) {
first = first.previous;
}
return first;
}
private boolean hasNext() {
return this.next != null;
}
private State next() {
return this.next;
}
private CompletableFuture future() {
return future.get();
}
private void future(CompletableFuture previousFuture) {
future.set(previousFuture);
}
private CompletableFuture previousFuture() {
return previous().future();
}
private Throwable unwrap(final Throwable t) {
Throwable inner = (t instanceof CompletionException) ? t.getCause() : t;
inner = (inner instanceof CancellationException) ? t.getCause() : inner;
return inner;
}
public CompletesId id() {
return id;
}
}
enum OutcomeType {
Some,
None
}
interface Outcome {
default boolean isCompleted() {
return false;
}
default T value() {
return null;
}
}
static class BaseOutcome implements Outcome {
@Override
public String toString() {
return getClass().getSimpleName() + " [ completed=" + isCompleted() + " value=" + value() + "]";
}
}
static class CompletedOutcome extends BaseOutcome {
private final AtomicReference outcome;
CompletedOutcome(final T outcome) {
this.outcome = new AtomicReference<>(outcome);
}
@Override
public boolean isCompleted() {
return true;
}
@Override
public T value() {
return outcome.get();
}
}
static class UncompletedOutcome extends BaseOutcome {
static final UncompletedOutcome> instance = new UncompletedOutcome<>();
@SuppressWarnings("unchecked")
static T instance() {
return (T) instance;
}
UncompletedOutcome() {
}
}
}