nl.talsmasoftware.context.core.concurrent.ContextAwareCompletableFuture Maven / Gradle / Ivy
/*
* Copyright 2016-2024 Talsma ICT
*
* 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 nl.talsmasoftware.context.core.concurrent;
import nl.talsmasoftware.context.ContextManagers;
import nl.talsmasoftware.context.ContextSnapshot;
import nl.talsmasoftware.context.functions.BiConsumerWithContext;
import nl.talsmasoftware.context.functions.BiFunctionWithContext;
import nl.talsmasoftware.context.functions.ConsumerWithContext;
import nl.talsmasoftware.context.functions.FunctionWithContext;
import nl.talsmasoftware.context.functions.RunnableWithContext;
import nl.talsmasoftware.context.functions.SupplierWithContext;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Executor;
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 static java.util.Objects.requireNonNull;
/**
* This class extends the standard {@link CompletableFuture} that was introduced in java version 8.
*
* The class is a 'normal' Completable Future, but every successive call made on the result will be made within the
* {@link ContextSnapshot context during creation} of this {@link ContextAwareCompletableFuture}.
*
* @author Sjoerd Talsma
*/
public class ContextAwareCompletableFuture extends CompletableFuture {
/**
* Holder for context snapshots to be propagated from one CompletionStage to the next.
*/
private final ContextSnapshotHolder snapshotHolder;
/**
* Whether to take a new snapshot after each completion stage.
*/
private final boolean takeNewSnapshot;
/**
* Creates a new {@link ContextSnapshot} and remembers that in this completable future,
* running all completion methods within this snapshot.
*
* @see ContextManagers#createContextSnapshot()
*/
public ContextAwareCompletableFuture() {
this((ContextSnapshot) null);
}
/**
* Creates a new {@link CompletableFuture} where all completion methods are run within the specified
* snapshot context.
*
* @param snapshot the snapshot to run completion methods in.
* Optional, the completable future will take a new snaphot if {@code null} is provided.
* @see ContextManagers#createContextSnapshot()
*/
public ContextAwareCompletableFuture(ContextSnapshot snapshot) {
this(new ContextSnapshotHolder(snapshot), false);
}
private ContextAwareCompletableFuture(ContextSnapshotHolder holder, boolean takeNewSnapshot) {
this.snapshotHolder = requireNonNull(holder, "Snapshot holder is ");
this.takeNewSnapshot = takeNewSnapshot;
}
/**
* Runs the {@code supplier} task in the common {@link java.util.concurrent.ForkJoinPool ForkJoinPool}
* within the current context and also applies that context to all successive
* calls to the {@code CompletableFuture}.
*
* @param supplier a function to be performed asynchronously returning the result of the CompletableFuture
* @param the function's return type
* @return The new CompletableFuture that propagates a snapshot of the current context
* @see CompletableFuture#supplyAsync(Supplier)
* @see ContextAwareCompletableFuture#supplyAsync(Supplier, Executor, ContextSnapshot, boolean)
*/
public static ContextAwareCompletableFuture supplyAsync(Supplier supplier) {
return supplyAsync(supplier, null, null, false);
}
/**
* Runs the {@code supplier} task in the specified {@link Executor executor}
* within the current context and also applies that context to all successive
* calls to the {@code CompletableFuture}.
*
* @param supplier a function returning the value to be used to complete the returned CompletableFuture
* @param executor the executor to use for asynchronous execution
* @param the function's return type
* @return The new CompletableFuture that propagates a snapshot of the current context
* @see CompletableFuture#supplyAsync(Supplier, Executor)
* @see ContextAwareCompletableFuture#supplyAsync(Supplier, Executor, ContextSnapshot, boolean)
*/
public static ContextAwareCompletableFuture supplyAsync(Supplier supplier, Executor executor) {
return supplyAsync(supplier, executor, null, false);
}
/**
* Runs the {@code supplier} task in the specified {@link Executor executor}
* within the specified {@link ContextSnapshot context snapshot} and also applies that context
* to all successive calls to the {@code CompletableFuture}.
*
* This method is lenient to {@code null} values for {@code executor} and {@code snapshot}:
* If {@code executor == null} the common {@link java.util.concurrent.ForkJoinPool ForkJoinPool} is used as
* specified by {@link CompletableFuture#supplyAsync(Supplier)}.
* If {@code snapshot == null} a {@link ContextManagers#createContextSnapshot() new context snapshot} is
* created for the {@link Supplier} (if not already a {@link SupplierWithContext}).
*
* @param supplier a function returning the value to be used to complete the returned CompletableFuture
* @param executor the executor to use for asynchronous execution
* @param snapshot a snapshot of the context to be propagated in the supplier function
* and all successive calls of this completable future
* @param the function's return type
* @return The new CompletableFuture that propagates the specified context snapshot
* @see CompletableFuture#supplyAsync(Supplier, Executor)
* @see ContextAwareCompletableFuture#supplyAsync(Supplier, Executor, ContextSnapshot, boolean)
*/
public static ContextAwareCompletableFuture supplyAsync(Supplier supplier, Executor executor, ContextSnapshot snapshot) {
return supplyAsync(supplier, executor, snapshot, false);
}
/**
* Runs the {@code supplier} task in the specified {@link Executor executor}
* within the specified {@link ContextSnapshot context snapshot} and also applies that context
* to all successive calls to the {@code CompletableFuture}.
*
* This method is lenient to {@code null} values for {@code executor} and {@code snapshot}:
* If {@code executor == null} the common {@link java.util.concurrent.ForkJoinPool ForkJoinPool} is used as
* specified by {@link CompletableFuture#supplyAsync(Supplier)}.
* If {@code snapshot == null} a {@link ContextManagers#createContextSnapshot() new context snapshot} is
* created for the {@link Supplier} (if not already a {@link SupplierWithContext}).
*
* @param supplier a function returning the value to be used to complete the returned CompletableFuture
* @param executor the executor to use for asynchronous execution
* @param snapshot a snapshot of the context to be propagated in the supplier function
* and all successive calls of this completable future
* @param takeNewSnapshot whether a new ContextSnapshot should be taken after the supplier function is done.
* If {@code false}, the snapshot from the caller propagate to all following completion stages.
* If {@code true}, a new snapshot is taken after each completion stage to propagate into the next.
* @param the function's return type
* @return The new CompletableFuture that propagates the specified context snapshot
* @see CompletableFuture#supplyAsync(Supplier, Executor)
* @since 1.0.4
*/
public static ContextAwareCompletableFuture supplyAsync(
Supplier supplier, Executor executor, ContextSnapshot snapshot, boolean takeNewSnapshot) {
final ContextSnapshotHolder holder = new ContextSnapshotHolder(snapshot);
supplier = new SupplierWithContext(holder, supplier, takeNewSnapshot ? holder : null) {
};
return wrap(executor == null
? CompletableFuture.supplyAsync(supplier)
: CompletableFuture.supplyAsync(supplier, executor),
holder,
takeNewSnapshot);
}
/**
* Runs the {@code runnable} task in the common {@link java.util.concurrent.ForkJoinPool ForkJoinPool}
* within the current context and also applies that context to all successive
* calls to the {@code CompletableFuture}.
*
* @param runnable the action to run before completing the returned CompletableFuture
* @return The new CompletableFuture that propagates a snapshot of the current context
* @see CompletableFuture#runAsync(Runnable)
* @see ContextAwareCompletableFuture#runAsync(Runnable, Executor, ContextSnapshot, boolean)
*/
public static ContextAwareCompletableFuture runAsync(Runnable runnable) {
return runAsync(runnable, null, null, false);
}
/**
* Runs the {@code runnable} task in the specified {@link Executor executor}
* within the current context and also applies that context to all successive
* calls to the {@code CompletableFuture}.
*
* @param runnable the action to run before completing the returned CompletableFuture
* @param executor the executor to use for asynchronous execution
* @return The new CompletableFuture that propagates a snapshot of the current context
* @see CompletableFuture#runAsync(Runnable, Executor)
* @see ContextAwareCompletableFuture#runAsync(Runnable, Executor, ContextSnapshot, boolean)
*/
public static ContextAwareCompletableFuture runAsync(Runnable runnable, Executor executor) {
return runAsync(runnable, executor, null, false);
}
/**
* Runs the {@code runnable} task in the specified {@link Executor executor}
* within the specified {@link ContextSnapshot context snapshot} and also applies that context
* to all successive calls to the {@code CompletableFuture}.
*
* This method is lenient to {@code null} values for {@code executor} and {@code snapshot}:
* If {@code executor == null} the common {@link java.util.concurrent.ForkJoinPool ForkJoinPool} is used as
* specified by {@link CompletableFuture#supplyAsync(Supplier)}.
* If {@code snapshot == null} a {@link ContextManagers#createContextSnapshot() new context snapshot} is
* created for the {@link Supplier} (if not already a {@link SupplierWithContext}).
*
* @param runnable the action to run before completing the returned CompletableFuture
* @param executor the executor to use for asynchronous execution
* @param snapshot the context snapshot to apply to the runnable action
* @return The new CompletableFuture that propagates a snapshot of the current context
* @see CompletableFuture#runAsync(Runnable, Executor)
* @see ContextAwareCompletableFuture#runAsync(Runnable, Executor, ContextSnapshot, boolean)
*/
public static ContextAwareCompletableFuture runAsync(Runnable runnable, Executor executor, ContextSnapshot snapshot) {
return runAsync(runnable, executor, snapshot, false);
}
/**
* Runs the {@code runnable} task in the specified {@link Executor executor}
* within the specified {@link ContextSnapshot context snapshot} and also applies that context
* to all successive calls to the {@code CompletableFuture}.
*
* This method is lenient to {@code null} values for {@code executor} and {@code snapshot}:
* If {@code executor == null} the common {@link java.util.concurrent.ForkJoinPool ForkJoinPool} is used as
* specified by {@link CompletableFuture#supplyAsync(Supplier)}.
* If {@code snapshot == null} a {@link ContextManagers#createContextSnapshot() new context snapshot} is
* created for the {@link Supplier} (if not already a {@link SupplierWithContext}).
*
* @param runnable the action to run before completing the returned CompletableFuture
* @param executor the executor to use for asynchronous execution
* @param snapshot the context snapshot to apply to the runnable action
* @param takeNewSnapshot whether a new ContextSnapshot should be taken after the supplier function is done.
* If {@code false}, the snapshot from the caller propagate to all following completion stages.
* If {@code true}, a new snapshot is taken after each completion stage to propagate into the next.
* @return The new CompletableFuture that propagates a snapshot of the current context
* @see CompletableFuture#runAsync(Runnable, Executor)
* @since 1.0.4
*/
public static ContextAwareCompletableFuture runAsync(
Runnable runnable, Executor executor, ContextSnapshot snapshot, boolean takeNewSnapshot) {
final ContextSnapshotHolder holder = new ContextSnapshotHolder(snapshot);
runnable = new RunnableWithContext(holder, runnable, takeNewSnapshot ? holder : null) {
};
return wrap(executor == null
? CompletableFuture.runAsync(runnable)
: CompletableFuture.runAsync(runnable, executor),
holder,
takeNewSnapshot);
}
/**
* Creates a new {@code ContextAwareCompletableFuture} from the already-completed value.
* A new {@linkplain ContextSnapshot} is taken and applied to all
* following {@linkplain CompletionStage completion stages}.
*
* @param value the value to return from the already-completed future.
* @param the type of the value
* @return New {@code ContextAwareCompletableFuture} returning the completed value
* and containing a new {@code ContextSnapshot}.
* @see #completedFuture(Object, ContextSnapshot)
* @since 1.0.5
*/
public static ContextAwareCompletableFuture completedFuture(U value) {
return completedFuture(value, null);
}
/**
* Creates a new {@code ContextAwareCompletableFuture} from the already-completed value.
* A new {@linkplain ContextSnapshot} is taken and applied to all
* following {@linkplain CompletionStage completion stages}.
*
* @param value the value to return from the already-completed future.
* @param snapshot the context snapshot to apply to following completion stages
* (optional, specify {@code null} to take a new snapshot)
* @param the type of the value
* @return New {@code ContextAwareCompletableFuture} returning the completed value
* and containing the specified {@code ContextSnapshot}.
* @since 1.0.5
*/
public static ContextAwareCompletableFuture completedFuture(U value, ContextSnapshot snapshot) {
final ContextAwareCompletableFuture completedFuture = new ContextAwareCompletableFuture<>(snapshot);
completedFuture.complete(value);
return completedFuture;
}
/**
* Creates a new {@code CompletionStage} from the already-completed value.
* A new {@linkplain ContextSnapshot} is taken and applied to all
* following {@linkplain CompletionStage completion stages}.
*
* @param value the value to return from the already-completed stage.
* @param the type of the value
* @return New {@code CompletionStage} returning the completed value
* and containing a new {@code ContextSnapshot}.
* @see #completedFuture(Object, ContextSnapshot)
* @since 1.0.5
*/
public static CompletionStage completedStage(U value) {
return completedFuture(value, null);
}
/**
* Creates a new {@code ContextAwareCompletableFuture} that is already completed
* exceptionally with the given exception.
* A new {@linkplain ContextSnapshot} is taken and applied to all
* following {@linkplain CompletionStage completion stages}.
*
* @param ex the exception
* @param the type of the value
* @return New {@code ContextAwareCompletableFuture} throwing the exception
* and containing a new {@code ContextSnapshot}.
* @see #failedFuture(Throwable, ContextSnapshot)
* @since 1.0.5
*/
public static ContextAwareCompletableFuture failedFuture(Throwable ex) {
return failedFuture(ex, null);
}
/**
* Creates a new {@code ContextAwareCompletableFuture} that is already completed
* exceptionally with the given exception.
* The specified {@code snapshot} is applied to all
* following {@linkplain CompletionStage completion stages}.
*
* @param ex the exception
* @param snapshot the context snapshot to apply to following completion stages
* (optional, specify {@code null} to take a new snapshot)
* @param the type of the value
* @return New {@code ContextAwareCompletableFuture} throwing the exception
* and containing the specified {@code snapshot}.
* @since 1.0.5
*/
public static ContextAwareCompletableFuture failedFuture(Throwable ex, ContextSnapshot snapshot) {
final ContextAwareCompletableFuture failedFuture = new ContextAwareCompletableFuture<>(snapshot);
failedFuture.completeExceptionally(ex);
return failedFuture;
}
/**
* Creates a new {@code CompletionStage} that is already completed
* exceptionally with the given exception.
* A new {@linkplain ContextSnapshot} is taken and applied to all
* following {@linkplain CompletionStage completion stages}.
*
* @param ex the exception
* @param the type of the value
* @return New {@code CompletionStage} throwing the exception
* and containing a new {@code ContextSnapshot}.
* @see #failedFuture(Throwable, ContextSnapshot)
* @since 1.0.5
*/
public static CompletionStage failedStage(Throwable ex) {
return failedFuture(ex, null);
}
/**
* Returns a new CompletableFuture that is completed when all of
* the given CompletableFutures complete. If any of the given
* CompletableFutures complete exceptionally, then the returned
* CompletableFuture also does so, with a CompletionException
* holding this exception as its cause. Otherwise, the results,
* if any, of the given CompletableFutures are not reflected in
* the returned CompletableFuture, but may be obtained by
* inspecting them individually. If no CompletableFutures are
* provided, returns a CompletableFuture completed with the value
* {@code null}.
*
* Among the applications of this method is to await completion
* of a set of independent CompletableFutures before continuing a
* program, as in: {@code CompletableFuture.allOf(c1, c2,
* c3).join();}.
*
* A new {@linkplain ContextSnapshot} is taken and applied to all
* following {@linkplain CompletionStage completion stages}.
*
* @param cfs the CompletableFutures
* @return A new {@code ContextAwareCompletableFuture} that is completed when all of the
* given CompletableFutures complete
* @throws NullPointerException if the array or any of its elements are {@code null}
* @since 1.0.5
*/
public static ContextAwareCompletableFuture allOf(CompletableFuture>... cfs) {
return allOf((ContextSnapshot) null, cfs);
}
/**
* Returns a new CompletableFuture that is completed when all of
* the given CompletableFutures complete. If any of the given
* CompletableFutures complete exceptionally, then the returned
* CompletableFuture also does so, with a CompletionException
* holding this exception as its cause. Otherwise, the results,
* if any, of the given CompletableFutures are not reflected in
* the returned CompletableFuture, but may be obtained by
* inspecting them individually. If no CompletableFutures are
* provided, returns a CompletableFuture completed with the value
* {@code null}.
*
* Among the applications of this method is to await completion
* of a set of independent CompletableFutures before continuing a
* program, as in: {@code CompletableFuture.allOf(c1, c2,
* c3).join();}.
*
* The specified {@linkplain ContextSnapshot} is applied to all
* following {@linkplain CompletionStage completion stages}.
*
* @param snapshot the context snapshot to apply to following completion stages
* (optional, specify {@code null} to take a new snapshot)
* @param cfs the CompletableFutures
* @return A new {@code ContextAwareCompletableFuture} that is completed when all of the
* given CompletableFutures complete
* @throws NullPointerException if the array or any of its elements are {@code null}
* @since 1.0.5
*/
public static ContextAwareCompletableFuture allOf(ContextSnapshot snapshot, CompletableFuture>... cfs) {
final ContextSnapshotHolder holder = new ContextSnapshotHolder(snapshot);
return wrap(CompletableFuture.allOf(cfs), holder, false);
}
/**
* Returns a new CompletableFuture that is completed when any of
* the given CompletableFutures complete, with the same result.
* Otherwise, if it completed exceptionally, the returned
* CompletableFuture also does so, with a CompletionException
* holding this exception as its cause. If no CompletableFutures
* are provided, returns an incomplete CompletableFuture.
*
* A new {@linkplain ContextSnapshot} is taken and applied to all
* following {@linkplain CompletionStage completion stages}.
*
* @param cfs the CompletableFutures
* @return a new CompletableFuture that is completed with the result or exception
* of any of the given CompletableFutures when one completes
* @throws NullPointerException if the array or any of its elements are {@code null}
* @since 1.0.5
*/
public static ContextAwareCompletableFuture