package org.warp.commonutils.concurrency.future; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.LinkedHashSet; import java.util.List; import java.util.Optional; import java.util.PriorityQueue; import java.util.Set; import java.util.TreeSet; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; import org.warp.commonutils.functional.BiCompletableFunction; import org.warp.commonutils.functional.CompletableFunction; import org.warp.commonutils.functional.IOCompletableFunction; import org.warp.commonutils.functional.IOSupplier; import org.warp.commonutils.functional.TriCompletableFunction; import org.warp.commonutils.type.FloatPriorityQueue; import org.warp.commonutils.type.ScoredValue; public class CompletableFutureUtils { /** * Safely get a CompletableFuture asynchronously */ public static CompletableFuture getCompletableFutureAsync(Supplier> completableFutureSupplier, Executor executor) { CompletableFuture cf = new CompletableFuture<>(); executor.execute(() -> { try { var cf2 = completableFutureSupplier.get(); cf2.whenComplete((result, error) -> { if (error == null) { cf.complete(result); } else { cf.completeExceptionally(error); } }); } catch (Exception ex) { cf.completeExceptionally(ex); } }); return cf; } /** * Safely get a CompletableFuture or a FailedFuture */ public static CompletableFuture getCompletableFuture(Supplier> completableFutureSupplier) { CompletableFuture cf; try { cf = completableFutureSupplier.get(); } catch (Exception ex) { cf = CompletableFuture.failedFuture(ex); } return cf; } /** * Safely get a CompletableFuture or a FailedFuture */ public static CompletableFuture getCompletableFuture(CompletableFunction completableFutureFunction, F value) { return getCompletableFuture(() -> completableFutureFunction.apply(value)); } /** * Safely get a CompletableFuture or a FailedFuture */ public static CompletableFuture getCompletableFutureSupply(CompletableFunction completableFutureFunction, Supplier valueSupplier) { return getCompletableFuture(() -> completableFutureFunction.apply(valueSupplier.get())); } /** * Safely get a CompletableFuture or a FailedFuture */ public static CompletableFuture getCompletableFuture(BiCompletableFunction completableFutureFunction, F1 value1, F2 value2) { return getCompletableFuture(() -> completableFutureFunction.apply(value1, value2)); } /** * Safely get a CompletableFuture or a FailedFuture */ public static CompletableFuture getCompletableFutureSupply(BiCompletableFunction completableFutureFunction, Supplier value1Supplier, Supplier value2Supplier) { return getCompletableFuture(() -> completableFutureFunction.apply(value1Supplier.get(), value2Supplier.get())); } /** * Safely get a CompletableFuture or a FailedFuture */ public static CompletableFuture getCompletableFuture(TriCompletableFunction completableFutureFunction, F1 value1, F2 value2, F3 value3) { return getCompletableFuture(() -> completableFutureFunction.apply(value1, value2, value3)); } /** * Safely get a CompletableFuture or a FailedFuture */ public static CompletableFuture getCompletableFutureSupply(TriCompletableFunction completableFutureFunction, Supplier value1Supplier, Supplier value2Supplier, Supplier value3Supplier) { return getCompletableFuture(() -> completableFutureFunction.apply(value1Supplier.get(), value2Supplier.get(), value3Supplier.get())); } //// /** * Safely get a CompletableFuture or a FailedFuture */ public static CompletableFuture getCompletableFutureIO(IOSupplier> completableFutureSupplier) { CompletableFuture cf; try { cf = completableFutureSupplier.get(); } catch (Exception ex) { cf = CompletableFuture.failedFuture(ex); } return cf; } /** * Safely get a CompletableFuture or a FailedFuture */ public static CompletableFuture getCompletableFutureIO(IOCompletableFunction completableFutureFunction, F value) { return getCompletableFutureIO(() -> completableFutureFunction.apply(value)); } /** * Safely get a CompletableFuture or a FailedFuture */ public static CompletableFuture getCompletableFutureIOSupply(IOCompletableFunction completableFutureFunction, IOSupplier valueSupplier) { return getCompletableFutureIO(() -> completableFutureFunction.apply(valueSupplier.get())); } /** * Aggregate multiple {@link CompletableFuture} lists into a single {@link CompletableFuture} list * * @param futureLists A collection of {@link CompletableFuture} lists. * @param List elements type * @return {@link CompletableFuture} list */ public static CompletableFuture> aggregateList(Collection>> futureLists) { final CompletableFuture> identityAggregatedResult = CompletableFuture.completedFuture(new ArrayList()); return futureLists.parallelStream().reduce(identityAggregatedResult, (currentAggregatedResult, futureList) -> { return currentAggregatedResult.thenApplyAsync((aggregatedList) -> { aggregatedList.addAll(futureList.join()); return aggregatedList; }); }); } /** * Creates a new empty collection of disaggregated future results future lists */ public static Collection>>> createDisaggregatedResultsList() { return new ArrayList<>(10); } /** * Add a * @param disaggregatedResults * @param result * @param */ public static void addDisaggregatedList( Collection>>> disaggregatedResults, CompletableFuture>> result) { disaggregatedResults.add(result); } /** * Add a result */ public static void addDisaggregatedListCast( Collection>>> disaggregatedResults, CompletableFuture>> result) { addDisaggregatedListCastForced(disaggregatedResults, result); } public static void addDisaggregatedListCastForced( Collection>>> disaggregatedResults, CompletableFuture>> result) { disaggregatedResults.add(result.thenApply((originalList) -> { List> resultList = new ArrayList<>(); for (CompletableFuture originalFuture : originalList) { resultList.add(originalFuture.thenApply((originalValue) -> { //noinspection unchecked return (T) originalValue; })); } return resultList; })); } /** * Aggregate multiple {@link CompletableFuture} lists into a single {@link CompletableFuture} list * * @param futureFloatPriorityQueues A collection of {@link CompletableFuture} lists. * @param List elements type * @return {@link CompletableFuture} list */ public static CompletableFuture> aggregatePq(Collection>> futureFloatPriorityQueues) { final CompletableFuture> identityAggregatedResult = CompletableFuture.completedFuture(new FloatPriorityQueue<>()); return futureFloatPriorityQueues.stream().reduce(identityAggregatedResult, (currentAggregatedResult, futureFloatPriorityQueue) -> { return currentAggregatedResult.thenApply((aggregatedFloatPriorityQueue) -> { var futureFloatPriorityQueueValues = futureFloatPriorityQueue.join(); if (futureFloatPriorityQueueValues == aggregatedFloatPriorityQueue) { return aggregatedFloatPriorityQueue; } futureFloatPriorityQueueValues.forEachItem(aggregatedFloatPriorityQueue::offer); return aggregatedFloatPriorityQueue; }); }); } /** * Creates a new empty collection of disaggregated future results future lists */ public static Collection>>> createDisaggregatedFutureResultsPq() { return FloatPriorityQueue.synchronizedPq(10); } /** * Creates a new empty collection of disaggregated results future lists */ public static Collection>> createDisaggregatedResultsPq() { return FloatPriorityQueue.synchronizedPq(10); } /** * Add a * @param disaggregatedResults * @param result * @param */ public static void addDisaggregatedPq( Collection>>> disaggregatedResults, CompletableFuture>> result) { disaggregatedResults.add(result); } /** * Add a result */ public static void addDisaggregatedPqCast( Collection>>> disaggregatedResults, CompletableFuture>> result) { addDisaggregatedPqCastForced(disaggregatedResults, result); } public static void addDisaggregatedFuturePqCastForced( Collection>>> disaggregatedResults, CompletableFuture>> result) { disaggregatedResults.add(result.thenApply((originalFloatPriorityQueue) -> { FloatPriorityQueue> resultFloatPriorityQueue = new FloatPriorityQueue<>(); originalFloatPriorityQueue.forEachItem((originalFuture) -> { resultFloatPriorityQueue.offer(ScoredValue.of(originalFuture.getScore(), originalFuture.getValue().thenApply((originalValue) -> { //noinspection unchecked return (T) originalValue; }) )); }); return resultFloatPriorityQueue; })); } public static void addDisaggregatedPqCastForced( Collection>> disaggregatedResults, CompletableFuture> result) { disaggregatedResults.add(result.thenApply((originalFloatPriorityQueue) -> { FloatPriorityQueue resultFloatPriorityQueue = new FloatPriorityQueue<>(); originalFloatPriorityQueue.forEachItem((originalFuture) -> { //noinspection unchecked resultFloatPriorityQueue.offer(ScoredValue.of(originalFuture.getScore(), (T) originalFuture.getValue())); }); return resultFloatPriorityQueue; })); } public static Set collectToSet(CompletableFuture>> futureList) { return futureList.join().parallelStream().map(CompletableFuture::join).collect(Collectors.toSet()); } public static Set collectToSet(CompletableFuture>> futureList, int limit) { return futureList.join().parallelStream().map(CompletableFuture::join).limit(10).collect(Collectors.toSet()); } public static List collectToList(CompletableFuture>> futureList) { return futureList.join().stream().map(CompletableFuture::join).collect(Collectors.toList()); } public static List collectToList(CompletableFuture>> futureList, int limit) { return futureList.join().stream().map(CompletableFuture::join).limit(limit).collect(Collectors.toList()); } public static LinkedHashSet collectToLinkedSetFuture(CompletableFuture>> futureList) { return futureList.join().stream().map(CompletableFuture::join).collect(Collectors.toCollection(LinkedHashSet::new)); } public static LinkedHashSet collectToLinkedSetFuture(CompletableFuture>> futureList, int limit) { return futureList.join().stream().map(CompletableFuture::join).limit(limit) .collect(Collectors.toCollection(LinkedHashSet::new)); } public static LinkedHashSet collectToLinkedSet(CompletableFuture> futureList) { return new LinkedHashSet<>(futureList.join()); } public static LinkedHashSet collectToLinkedSet(CompletableFuture> futureList, int limit) { return futureList.join().stream().limit(limit) .collect(Collectors.toCollection(LinkedHashSet::new)); } public static FloatPriorityQueue collectToPq(CompletableFuture>> futureList) { var internalPq = futureList.join().streamItems().map(t -> { if (t.getValue() != null) { return ScoredValue.of(t.getScore(), t.getValue().join()); } else { return ScoredValue.of(t.getScore(), (T) null); } }).collect(Collectors.toCollection(PriorityQueue::new)); return new FloatPriorityQueue<>(internalPq); } public static FloatPriorityQueue collectToPq(CompletableFuture>> futureList, int limit) { var internalPq = futureList.join().streamItems().map(t -> { if (t.getValue() != null) { return ScoredValue.of(t.getScore(), t.getValue().join()); } else { return ScoredValue.of(t.getScore(), (T) null); } }).limit(limit).collect(Collectors.toCollection(PriorityQueue::new)); return new FloatPriorityQueue<>(internalPq); } public static TreeSet collectToTreeSetFuture(CompletableFuture>> futureList) { return futureList.join().stream().map(CompletableFuture::join).collect(Collectors.toCollection(TreeSet::new)); } public static TreeSet collectToTreeSetFuture(CompletableFuture>> futureList, int limit) { return futureList.join().stream().map(CompletableFuture::join).limit(limit) .collect(Collectors.toCollection(TreeSet::new)); } public static TreeSet collectToTreeSetFuture(CompletableFuture>> futureList, Comparator comparator) { return futureList.join().stream().map(CompletableFuture::join).collect(Collectors.toCollection(() -> new TreeSet<>(comparator))); } public static TreeSet collectToTreeSetFuture(CompletableFuture>> futureList, Comparator comparator, int limit) { return futureList.join().stream().map(CompletableFuture::join).limit(limit) .collect(Collectors.toCollection(() -> new TreeSet<>(comparator))); } public static TreeSet collectToTreeSet(CompletableFuture> futureList) { return new TreeSet<>(futureList.join()); } public static TreeSet collectToTreeSet(CompletableFuture> futureList, int limit) { return futureList.join().stream().limit(limit) .collect(Collectors.toCollection(TreeSet::new)); } public static TreeSet collectToTreeSet(CompletableFuture> futureList, Comparator comparator) { return futureList.join().stream().collect(Collectors.toCollection(() -> new TreeSet<>(comparator))); } public static TreeSet collectToTreeSet(CompletableFuture> futureList, Comparator comparator, int limit) { return futureList.join().stream().limit(limit) .collect(Collectors.toCollection(() -> new TreeSet<>(comparator))); } public static Optional anyOrNull(CompletableFuture>> futureList) { return futureList.join().parallelStream().map(CompletableFuture::join).findAny(); } public static Optional firstOrNullFuture(CompletableFuture>> futureList) { return futureList.join().stream().map(CompletableFuture::join).findFirst(); } public static Optional firstOrNull(CompletableFuture> futureList) { return futureList.join().stream().findFirst(); } public static void forEachOrdered(CompletableFuture>> futureList, Consumer consumer) { var futures = futureList.join(); futures.stream().map(CompletableFuture::join).forEachOrdered(consumer); } public static void forEachOrdered(CompletableFuture>> futureList, Consumer consumer, boolean reverse) { var futures = futureList.join(); if (reverse) { Collections.reverse(futures); } futures.stream().map(CompletableFuture::join).forEachOrdered(consumer); } public static void forEach(CompletableFuture>> futureList, Consumer consumer) { futureList.join().parallelStream().map(CompletableFuture::join).forEach(consumer); } /** * Use CompletableFutureUtils.getCompletableFuture(supplier); */ @Deprecated public static CompletableFuture catchUncheckedExceptions(Supplier> supplier) { return getCompletableFuture(supplier); } public static CompletableFuture runSequence(Collection> collection) { if (collection.isEmpty()) { return CompletableFuture.completedFuture(null); } else { var result = new CompletableFuture(); for (CompletableFuture completableFuture : collection) { result = result.thenCompose(x -> completableFuture.thenRun(() -> {})); } return result; } } public static CompletableFuture runSequenceAsync(Collection> collection, ExecutorService executorService) { var result = CompletableFuture.completedFuture(null); for (CompletableFuture completableFuture : collection) { result = result.thenComposeAsync(x -> completableFuture.thenRun(() -> {}), executorService); } return result; } /** * Accept values synchronously from an async sequence */ public static CompletableFuture acceptSequenceAsync(Collection> collection, Function> runner, ExecutorService executorService) { CompletableFuture result = CompletableFuture.completedFuture(null); for (CompletableFuture completableFuture : collection) { result = result.thenComposeAsync(x -> completableFuture.thenComposeAsync(runner::apply, executorService), executorService ); } return result; } /** * Accept values synchronously from an async sequence */ public static CompletableFuture acceptSequenceAsync(Collection> collection, Consumer runner, ExecutorService executorService) { CompletableFuture result = CompletableFuture.completedFuture(null); for (CompletableFuture completableFuture : collection) { result = result.thenComposeAsync(x -> completableFuture.thenAcceptAsync(runner, executorService), executorService); } return result; } public static CompletableFuture applySequenceAsync(T initialValue, Collection>> collection, ExecutorService executorService) { var result = CompletableFuture.completedFuture(initialValue); for (Function> item : collection) { result = result.thenComposeAsync(item, executorService); } return result; } public static CompletableFuture composeAsync( Supplier> supp, Executor executor) { return CompletableFuture.completedFuture(null).thenComposeAsync((_x) -> supp.get(), executor); } public static CompletableFuture composeAsyncIO( IOSupplier> supp, Executor executor) { return CompletableFuture.completedFuture(null).thenComposeAsync((_x) -> getCompletableFutureIO(supp), executor); } }