From be0220be462616071f7a04c2be0c05980ad612ed Mon Sep 17 00:00:00 2001 From: Thim Lohse Date: Mon, 27 Nov 2023 11:40:39 +0100 Subject: [PATCH 1/4] adding basic testing --- .../spotify/futures/CompletableFutures.java | 507 +++++++++--------- .../futures/CompletableFuturesTest.java | 27 + 2 files changed, 293 insertions(+), 241 deletions(-) diff --git a/src/main/java/com/spotify/futures/CompletableFutures.java b/src/main/java/com/spotify/futures/CompletableFutures.java index 4d06c7d..e3d8c37 100644 --- a/src/main/java/com/spotify/futures/CompletableFutures.java +++ b/src/main/java/com/spotify/futures/CompletableFutures.java @@ -7,9 +7,9 @@ * 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. @@ -34,6 +34,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionStage; +import java.util.concurrent.Executor; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -43,8 +44,8 @@ import java.util.stream.Collector; /** - * A collection of static utility methods that extend the - * {@link java.util.concurrent.CompletableFuture Java completable future} API. + * A collection of static utility methods that extend the {@link + * java.util.concurrent.CompletableFuture Java completable future} API. * * @since 0.1.0 */ @@ -56,16 +57,16 @@ private CompletableFutures() { /** * Returns a new {@link CompletableFuture} which completes to a list of all values of its input - * stages, if all succeed. The list of results is in the same order as the input stages. + * stages, if all succeed. The list of results is in the same order as the input stages. * - *

As soon as any of the given stages complete exceptionally, then the returned future also does so, - * with a {@link CompletionException} holding this exception as its cause. + *

As soon as any of the given stages complete exceptionally, then the returned future also + * does so, with a {@link CompletionException} holding this exception as its cause. * - *

If no stages are provided, returns a future holding an empty list. + *

If no stages are provided, returns a future holding an empty list. * * @param stages the stages to combine - * @param the common super-type of all of the input stages, that determines the monomorphic - * type of the output future + * @param the common super-type of all of the input stages, that determines the monomorphic + * type of the output future * @return a future that completes to a list of the results of the supplied stages * @throws NullPointerException if the stages list or any of its elements are {@code null} * @since 0.1.0 @@ -84,16 +85,17 @@ public static CompletableFuture> allAsList( CompletableFuture allOf = CompletableFuture.allOf(all); for (int i = 0; i < all.length; i++) { - all[i].exceptionally(throwable -> { - if (!allOf.isDone()) { - allOf.completeExceptionally(throwable); - } - return null; // intentionally unused - }); + all[i].exceptionally( + throwable -> { + if (!allOf.isDone()) { + allOf.completeExceptionally(throwable); + } + return null; // intentionally unused + }); } - return allOf - .thenApply(ignored -> { + return allOf.thenApply( + ignored -> { final List result = new ArrayList<>(all.length); for (int i = 0; i < all.length; i++) { result.add(all[i].join()); @@ -106,15 +108,15 @@ public static CompletableFuture> allAsList( * Returns a new {@link CompletableFuture} which completes to a map of all values of its input * stages, if all succeed. * - *

If any of the given stages complete exceptionally, then the returned future also does so, + *

If any of the given stages complete exceptionally, then the returned future also does so, * with a {@link CompletionException} holding this exception as its cause. * - *

If no stages are provided, returns a future holding an empty map. + *

If no stages are provided, returns a future holding an empty map. * * @param map the map of stages to combine - * @param the common super-type of the keys - * @param the common super-type of all of the input value-stages, that determines the - * monomorphic type of the output future + * @param the common super-type of the keys + * @param the common super-type of all of the input value-stages, that determines the + * monomorphic type of the output future * @return a future that completes to a map of the results of the supplied keys and value-stages * @throws NullPointerException if value-stages or any of its elements are {@code null} * @since 0.3.3 @@ -149,28 +151,25 @@ public static CompletableFuture> allAsMap( * * @param stages the stages to combine. * @param defaultValueMapper a function that will be called when a future completes exceptionally - * to provide a default value to place in the resulting list - * @param the common type of all of the input stages, that determines the type of the - * output future + * to provide a default value to place in the resulting list + * @param the common type of all of the input stages, that determines the type of the output + * future * @return a future that completes to a list of the results of the supplied stages * @throws NullPointerException if the stages list or any of its elements are {@code null} */ public static CompletableFuture> successfulAsList( List> stages, Function defaultValueMapper) { - return stages.stream() - .map(f -> f.exceptionally(defaultValueMapper)) - .collect(joinList()); + return stages.stream().map(f -> f.exceptionally(defaultValueMapper)).collect(joinList()); } /** - * Returns a new {@code CompletableFuture} that is already exceptionally completed with - * the given exception. + * Returns a new {@code CompletableFuture} that is already exceptionally completed with the given + * exception. * * @param throwable the exception - * @param an arbitrary type for the returned future; can be anything since the future - * will be exceptionally completed and thus there will never be a value of type - * {@code T} + * @param an arbitrary type for the returned future; can be anything since the future will be + * exceptionally completed and thus there will never be a value of type {@code T} * @return a future that exceptionally completed with the supplied exception * @throws NullPointerException if the supplied throwable is {@code null} * @since 0.1.0 @@ -182,10 +181,10 @@ public static CompletableFuture exceptionallyCompletedFuture(Throwable th } /** - * Collect a stream of {@link CompletionStage}s into a single future holding a list of the - * joined entities. + * Collect a stream of {@link CompletionStage}s into a single future holding a list of the joined + * entities. * - *

Usage: + *

Usage: * *

{@code
    * collection.stream()
@@ -194,22 +193,22 @@ public static  CompletableFuture exceptionallyCompletedFuture(Throwable th
    *     .thenApply(this::consumeList)
    * }
* - *

The generated {@link CompletableFuture} will complete to a list of all entities, in the - * order they were encountered in the original stream. Similar to - * {@link CompletableFuture#allOf(CompletableFuture[])}, if any of the input futures complete - * exceptionally, then the returned CompletableFuture also does so, with a - * {@link CompletionException} holding this exception as its cause. + *

The generated {@link CompletableFuture} will complete to a list of all entities, in the + * order they were encountered in the original stream. Similar to {@link + * CompletableFuture#allOf(CompletableFuture[])}, if any of the input futures complete + * exceptionally, then the returned CompletableFuture also does so, with a {@link + * CompletionException} holding this exception as its cause. * * @param the common super-type of all of the input stages, that determines the monomorphic - * type of the output future + * type of the output future * @param the implementation of {@link CompletionStage} that the stream contains * @return a new {@link CompletableFuture} according to the rules outlined in the method - * description + * description * @throws NullPointerException if any future in the stream is {@code null} * @since 0.1.0 */ public static > - Collector>> joinList() { + Collector>> joinList() { return collectingAndThen(toList(), CompletableFutures::allAsList); } @@ -218,7 +217,7 @@ public static CompletableFuture exceptionallyCompletedFuture(Throwable th * applying the provided mapping function to the input elements, and whose values are the * corresponding joined entities. * - *

Usage: + *

Usage: * *

{@code
    * collection.stream()
@@ -226,27 +225,27 @@ public static  CompletableFuture exceptionallyCompletedFuture(Throwable th
    *     .thenApply(this::consumeMap)
    * }
* - *

The generated {@link CompletableFuture} will complete to a Map of all entities. Similar - * to {@link CompletableFuture#allOf(CompletableFuture[])}, if any of the input futures - * complete exceptionally, then the returned CompletableFuture also does so, with a - * {@link CompletionException} holding this exception as its cause. + *

The generated {@link CompletableFuture} will complete to a Map of all entities. Similar to + * {@link CompletableFuture#allOf(CompletableFuture[])}, if any of the input futures complete + * exceptionally, then the returned CompletableFuture also does so, with a {@link + * CompletionException} holding this exception as its cause. * * @param keyMapper a mapping function to produce keys - * @param valueFutureMapper a mapping function to produce futures that will eventually produce - * the values + * @param valueFutureMapper a mapping function to produce futures that will eventually produce the + * values * @param the type of the input elements * @param the output type of the key mapping function * @param the common super-type of the stages returned by the value future mapping function * @return a new {@link CompletableFuture} according to the rules outlined in the method - * description + * description * @throws NullPointerException if valueFutureMapper returns {@code null} for any input element * @since 0.3.4 */ public static Collector>> joinMap( - Function keyMapper, - Function> valueFutureMapper){ + Function keyMapper, + Function> valueFutureMapper) { Collector>> collector = - toMap(keyMapper, valueFutureMapper); + toMap(keyMapper, valueFutureMapper); return collectingAndThen(collector, CompletableFutures::allAsMap); } @@ -267,7 +266,7 @@ public static void checkCompleted(CompletionStage stage) { * Gets the value of a completed stage. * * @param stage a completed {@link CompletionStage} - * @param the type of the value that the stage completes into + * @param the type of the value that the stage completes into * @return the value of the stage if it has one * @throws IllegalStateException if the stage is not completed * @since 0.1.0 @@ -280,13 +279,14 @@ public static T getCompleted(CompletionStage stage) { /** * Gets the exception from an exceptionally completed future + * * @param stage an exceptionally completed {@link CompletionStage} - * @param the type of the value that the stage completes into + * @param the type of the value that the stage completes into * @return the exception the stage has completed with * @throws IllegalStateException if the stage is not completed exceptionally * @throws CancellationException if the stage was cancelled - * @throws UnsupportedOperationException if the {@link CompletionStage} does not - * support the {@link CompletionStage#toCompletableFuture()} operation + * @throws UnsupportedOperationException if the {@link CompletionStage} does not support the + * {@link CompletionStage#toCompletableFuture()} operation */ public static Throwable getException(CompletionStage stage) { CompletableFuture future = stage.toCompletableFuture(); @@ -305,60 +305,88 @@ public static Throwable getException(CompletionStage stage) { * Returns a new stage that, when this stage completes either normally or exceptionally, is * executed with this stage's result and exception as arguments to the supplied function. * - *

When this stage is complete, the given function is invoked with the result (or {@code null} + *

When this stage is complete, the given function is invoked with the result (or {@code null} * if none) and the exception (or {@code null} if none) of this stage as arguments, and the * function's result is used to complete the returned stage. * - *

This differs from - * {@link java.util.concurrent.CompletionStage#handle(java.util.function.BiFunction)} in that the + *

This differs from {@link + * java.util.concurrent.CompletionStage#handle(java.util.function.BiFunction)} in that the * function should return a {@link java.util.concurrent.CompletionStage} rather than the value * directly. * * @param stage the {@link CompletionStage} to compose - * @param fn the function to use to compute the value of the - * returned {@link CompletionStage} - * @param the type of the input stage's value. - * @param the function's return type + * @param fn the function to use to compute the value of the returned {@link CompletionStage} + * @param the type of the input stage's value. + * @param the function's return type * @return the new {@link CompletionStage} * @since 0.1.0 */ public static CompletionStage handleCompose( - CompletionStage stage, - BiFunction> fn) { + CompletionStage stage, BiFunction> fn) { return dereference(stage.handle(fn)); } /** - * Returns a new stage that, when this stage completes - * exceptionally, is executed with this stage's exception as the - * argument to the supplied function. Otherwise, if this stage - * completes normally, then the returned stage also completes - * normally with the same value. + * Returns a new stage that, when this stage completes exceptionally, is executed with this + * stage's exception as the argument to the supplied function. Otherwise, if this stage completes + * normally, then the returned stage also completes normally with the same value. * - *

This differs from - * {@link java.util.concurrent.CompletionStage#exceptionally(java.util.function.Function)} - * in that the function should return a {@link java.util.concurrent.CompletionStage} rather than - * the value directly. + *

This differs from {@link + * java.util.concurrent.CompletionStage#exceptionally(java.util.function.Function)} in that the + * function should return a {@link java.util.concurrent.CompletionStage} rather than the value + * directly. * * @param stage the {@link CompletionStage} to compose - * @param fn the function to use to compute the value of the - * returned {@link CompletionStage} if this stage completed - * exceptionally - * @param the type of the input stage's value. + * @param fn the function to use to compute the value of the returned {@link CompletionStage} if + * this stage completed exceptionally + * @param the type of the input stage's value. * @return the new {@link CompletionStage} * @since 0.1.0 */ public static CompletionStage exceptionallyCompose( - CompletionStage stage, - Function> fn) { + CompletionStage stage, Function> fn) { return dereference(wrap(stage).exceptionally(fn)); } + /** + * This allows for a stage to be the return type of supplier when using supplyAsync and returns a plain stage. + * + *

This differs from {@link + * java.util.concurrent.CompletableFuture#supplyAsync(Supplier)} in that the + * function should return a {@link java.util.concurrent.CompletionStage} rather than the {@link CompletionStage} of + * a {@link CompletionStage} of a value when the return value of the {@link Supplier} is a {@link CompletionStage}. + * + * @param supplier a {@link Supplier} with a return value of {@link CompletionStage} + * @param the type of the supplied stage's value + * @return the new {@link CompletionStage} + */ + public static CompletionStage supplyAsyncCompose(Supplier> supplier) { + return dereference(CompletableFuture.supplyAsync(supplier)); + } + + /** + * This allows for a stage to be the return type of supplier when using supplyAsync and returns a plain stage. + * + *

This differs from {@link + * java.util.concurrent.CompletableFuture#supplyAsync(Supplier, Executor)} in that the + * function should return a {@link java.util.concurrent.CompletionStage} rather than the {@link CompletionStage} of + * a {@link CompletionStage} of a value when the return value of the {@link Supplier} is a {@link CompletionStage}. + * + * @param supplier a {@link Supplier} with a return value of {@link CompletionStage} + * @param executor a {@link Executor} the executor to use for asynchronous execution + * @param the type of the supplied stage's value + * @return the new {@link CompletionStage} + */ + public static CompletionStage supplyAsyncCompose( + Supplier> supplier, Executor executor) { + return dereference(CompletableFuture.supplyAsync(supplier, executor)); + } + /** * This takes a stage of a stage of a value and returns a plain stage of a value. * * @param stage a {@link CompletionStage} of a {@link CompletionStage} of a value - * @param the type of the inner stage's value. + * @param the type of the inner stage's value. * @return the {@link CompletionStage} of the value * @since 0.1.0 */ @@ -375,37 +403,38 @@ private static CompletionStage> wrap(CompletionStage f /** * Combines multiple stages by applying a function. * - * @param a the first stage. - * @param b the second stage. + * @param a the first stage. + * @param b the second stage. * @param function the combining function. - * @param the type of the combining function's return value. - * @param the type of the first stage's value. - * @param the type of the second stage's value. + * @param the type of the combining function's return value. + * @param the type of the first stage's value. + * @param the type of the second stage's value. * @return a stage that completes into the return value of the supplied function. * @since 0.1.0 */ public static CompletionStage combine( - CompletionStage a, CompletionStage b, - BiFunction function) { + CompletionStage a, CompletionStage b, BiFunction function) { return a.thenCombine(b, function); } /** * Combines multiple stages by applying a function. * - * @param a the first stage. - * @param b the second stage. - * @param c the third stage. + * @param a the first stage. + * @param b the second stage. + * @param c the third stage. * @param function the combining function. - * @param the type of the combining function's return value. - * @param the type of the first stage's value. - * @param the type of the second stage's value. - * @param the type of the third stage's value. + * @param the type of the combining function's return value. + * @param the type of the first stage's value. + * @param the type of the second stage's value. + * @param the type of the third stage's value. * @return a stage that completes into the return value of the supplied function. * @since 0.1.0 */ public static CompletionStage combine( - CompletionStage a, CompletionStage b, CompletionStage c, + CompletionStage a, + CompletionStage b, + CompletionStage c, Function3 function) { final CompletableFuture af = a.toCompletableFuture(); final CompletableFuture bf = b.toCompletableFuture(); @@ -418,21 +447,24 @@ public static CompletionStage combine( /** * Combines multiple stages by applying a function. * - * @param a the first stage. - * @param b the second stage. - * @param c the third stage. - * @param d the fourth stage. + * @param a the first stage. + * @param b the second stage. + * @param c the third stage. + * @param d the fourth stage. * @param function the combining function. - * @param the type of the combining function's return value. - * @param the type of the first stage's value. - * @param the type of the second stage's value. - * @param the type of the third stage's value. - * @param the type of the fourth stage's value. + * @param the type of the combining function's return value. + * @param the type of the first stage's value. + * @param the type of the second stage's value. + * @param the type of the third stage's value. + * @param the type of the fourth stage's value. * @return a stage that completes into the return value of the supplied function. * @since 0.1.0 */ public static CompletionStage combine( - CompletionStage a, CompletionStage b, CompletionStage c, CompletionStage d, + CompletionStage a, + CompletionStage b, + CompletionStage c, + CompletionStage d, Function4 function) { final CompletableFuture af = a.toCompletableFuture(); final CompletableFuture bf = b.toCompletableFuture(); @@ -446,24 +478,27 @@ public static CompletionStage combine( /** * Combines multiple stages by applying a function. * - * @param a the first stage. - * @param b the second stage. - * @param c the third stage. - * @param d the fourth stage. - * @param e the fifth stage. + * @param a the first stage. + * @param b the second stage. + * @param c the third stage. + * @param d the fourth stage. + * @param e the fifth stage. * @param function the combining function. - * @param the type of the combining function's return value. - * @param the type of the first stage's value. - * @param the type of the second stage's value. - * @param the type of the third stage's value. - * @param the type of the fourth stage's value. - * @param the type of the fifth stage's value. + * @param the type of the combining function's return value. + * @param the type of the first stage's value. + * @param the type of the second stage's value. + * @param the type of the third stage's value. + * @param the type of the fourth stage's value. + * @param the type of the fifth stage's value. * @return a stage that completes into the return value of the supplied function. * @since 0.1.0 */ public static CompletionStage combine( - CompletionStage a, CompletionStage b, CompletionStage c, - CompletionStage d, CompletionStage e, + CompletionStage a, + CompletionStage b, + CompletionStage c, + CompletionStage d, + CompletionStage e, Function5 function) { final CompletableFuture af = a.toCompletableFuture(); final CompletableFuture bf = b.toCompletableFuture(); @@ -472,33 +507,37 @@ public static CompletionStage combine( final CompletableFuture ef = e.toCompletableFuture(); return CompletableFuture.allOf(af, bf, cf, df, ef) - .thenApply(ignored -> - function.apply(af.join(), bf.join(), cf.join(), df.join(), ef.join())); + .thenApply( + ignored -> function.apply(af.join(), bf.join(), cf.join(), df.join(), ef.join())); } /** * Combines multiple stages by applying a function. * - * @param a the first stage. - * @param b the second stage. - * @param c the third stage. - * @param d the fourth stage. - * @param e the fifth stage. - * @param f the sixth stage. + * @param a the first stage. + * @param b the second stage. + * @param c the third stage. + * @param d the fourth stage. + * @param e the fifth stage. + * @param f the sixth stage. * @param function the combining function. - * @param the type of the combining function's return value. - * @param the type of the first stage's value. - * @param the type of the second stage's value. - * @param the type of the third stage's value. - * @param the type of the fourth stage's value. - * @param the type of the fifth stage's value. - * @param the type of the sixth stage's value. + * @param the type of the combining function's return value. + * @param the type of the first stage's value. + * @param the type of the second stage's value. + * @param the type of the third stage's value. + * @param the type of the fourth stage's value. + * @param the type of the fifth stage's value. + * @param the type of the sixth stage's value. * @return a stage that completes into the return value of the supplied function. * @since 0.3.2 */ public static CompletionStage combine( - CompletionStage a, CompletionStage b, CompletionStage c, - CompletionStage d, CompletionStage e, CompletionStage f, + CompletionStage a, + CompletionStage b, + CompletionStage c, + CompletionStage d, + CompletionStage e, + CompletionStage f, Function6 function) { final CompletableFuture af = a.toCompletableFuture(); final CompletableFuture bf = b.toCompletableFuture(); @@ -508,26 +547,22 @@ public static CompletionStage combine( final CompletableFuture ff = f.toCompletableFuture(); return CompletableFuture.allOf(af, bf, cf, df, ef, ff) - .thenApply(ignored -> - function.apply(af.join(), - bf.join(), - cf.join(), - df.join(), - ef.join(), - ff.join())); + .thenApply( + ignored -> + function.apply(af.join(), bf.join(), cf.join(), df.join(), ef.join(), ff.join())); } /** * Combines multiple stages by applying a function. * * @param function the combining function. - * @param stages the stages to combine - * @param the type of the combining function's return value. + * @param stages the stages to combine + * @param the type of the combining function's return value. * @return a stage that completes into the return value of the supplied function. * @since 0.4.0 */ public static CompletionStage combine( - Function function, CompletionStage... stages) { + Function function, CompletionStage... stages) { return combine(function, Arrays.asList(stages)); } @@ -535,38 +570,37 @@ public static CompletionStage combine( * Combines multiple stages by applying a function. * * @param function the combining function. - * @param stages the stages to combine - * @param the type of the combining function's return value. + * @param stages the stages to combine + * @param the type of the combining function's return value. * @return a stage that completes into the return value of the supplied function. * @since 0.4.0 */ public static CompletionStage combine( - Function function, List> stages) { + Function function, List> stages) { @SuppressWarnings("unchecked") // generic array creation final CompletableFuture[] all = new CompletableFuture[stages.size()]; for (int i = 0; i < stages.size(); i++) { all[i] = stages.get(i).toCompletableFuture(); } - return CompletableFuture.allOf(all).thenApply(ignored -> function.apply(new CombinedFutures(stages))); + return CompletableFuture.allOf(all) + .thenApply(ignored -> function.apply(new CombinedFutures(stages))); } /** * Composes multiple stages into another stage using a function. * - * @param a the first stage. - * @param b the second stage. + * @param a the first stage. + * @param b the second stage. * @param function the combining function. - * @param the type of the composed {@link CompletionStage}. - * @param the type of the first stage's value. - * @param the type of the second stage's value. + * @param the type of the composed {@link CompletionStage}. + * @param the type of the first stage's value. + * @param the type of the second stage's value. * @return a stage that is composed from the input stages using the function. - * @throws UnsupportedOperationException if any of the {@link CompletionStage}s - * do not interoperate with CompletableFuture + * @throws UnsupportedOperationException if any of the {@link CompletionStage}s do not + * interoperate with CompletableFuture */ public static CompletionStage combineFutures( - CompletionStage a, - CompletionStage b, - BiFunction> function) { + CompletionStage a, CompletionStage b, BiFunction> function) { final CompletableFuture af = a.toCompletableFuture(); final CompletableFuture bf = b.toCompletableFuture(); @@ -577,17 +611,17 @@ public static CompletionStage combineFutures( /** * Composes multiple stages into another stage using a function. * - * @param a the first stage. - * @param b the second stage. - * @param c the third stage. + * @param a the first stage. + * @param b the second stage. + * @param c the third stage. * @param function the combining function. - * @param the type of the composed {@link CompletionStage}. - * @param the type of the first stage's value. - * @param the type of the second stage's value. - * @param the type of the third stage's value. + * @param the type of the composed {@link CompletionStage}. + * @param the type of the first stage's value. + * @param the type of the second stage's value. + * @param the type of the third stage's value. * @return a stage that is composed from the input stages using the function. - * @throws UnsupportedOperationException if any of the {@link CompletionStage}s - * do not interoperate with CompletableFuture + * @throws UnsupportedOperationException if any of the {@link CompletionStage}s do not + * interoperate with CompletableFuture */ public static CompletionStage combineFutures( CompletionStage a, @@ -599,27 +633,25 @@ public static CompletionStage combineFutures( final CompletableFuture cf = c.toCompletableFuture(); return CompletableFuture.allOf(af, bf, cf) - .thenCompose(ignored -> function.apply(af.join(), - bf.join(), - cf.join())); + .thenCompose(ignored -> function.apply(af.join(), bf.join(), cf.join())); } /** * Composes multiple stages into another stage using a function. * - * @param a the first stage. - * @param b the second stage. - * @param c the third stage. - * @param d the fourth stage. + * @param a the first stage. + * @param b the second stage. + * @param c the third stage. + * @param d the fourth stage. * @param function the combining function. - * @param the type of the composed {@link CompletionStage}. - * @param the type of the first stage's value. - * @param the type of the second stage's value. - * @param the type of the third stage's value. - * @param the type of the fourth stage's value. + * @param the type of the composed {@link CompletionStage}. + * @param the type of the first stage's value. + * @param the type of the second stage's value. + * @param the type of the third stage's value. + * @param the type of the fourth stage's value. * @return a stage that is composed from the input stages using the function. - * @throws UnsupportedOperationException if any of the {@link CompletionStage}s - * do not interoperate with CompletableFuture + * @throws UnsupportedOperationException if any of the {@link CompletionStage}s do not + * interoperate with CompletableFuture */ public static CompletionStage combineFutures( CompletionStage a, @@ -639,21 +671,21 @@ public static CompletionStage combineFutures( /** * Composes multiple stages into another stage using a function. * - * @param a the first stage. - * @param b the second stage. - * @param c the third stage. - * @param d the fourth stage. - * @param e the fifth stage. + * @param a the first stage. + * @param b the second stage. + * @param c the third stage. + * @param d the fourth stage. + * @param e the fifth stage. * @param function the combining function. - * @param the type of the composed {@link CompletionStage}. - * @param the type of the first stage's value. - * @param the type of the second stage's value. - * @param the type of the third stage's value. - * @param the type of the fourth stage's value. - * @param the type of the fifth stage's value. + * @param the type of the composed {@link CompletionStage}. + * @param the type of the first stage's value. + * @param the type of the second stage's value. + * @param the type of the third stage's value. + * @param the type of the fourth stage's value. + * @param the type of the fifth stage's value. * @return a stage that is composed from the input stages using the function. - * @throws UnsupportedOperationException if any of the {@link CompletionStage}s - * do not interoperate with CompletableFuture + * @throws UnsupportedOperationException if any of the {@link CompletionStage}s do not + * interoperate with CompletableFuture */ public static CompletionStage combineFutures( CompletionStage a, @@ -669,33 +701,30 @@ public static CompletionStage combineFutures( final CompletableFuture ef = e.toCompletableFuture(); return CompletableFuture.allOf(af, bf, cf, df, ef) - .thenCompose(ignored -> function.apply(af.join(), - bf.join(), - cf.join(), - df.join(), - ef.join())); + .thenCompose( + ignored -> function.apply(af.join(), bf.join(), cf.join(), df.join(), ef.join())); } /** * Composes multiple stages into another stage using a function. * - * @param a the first stage. - * @param b the second stage. - * @param c the third stage. - * @param d the fourth stage. - * @param e the fifth stage. - * @param f the sixth stage. + * @param a the first stage. + * @param b the second stage. + * @param c the third stage. + * @param d the fourth stage. + * @param e the fifth stage. + * @param f the sixth stage. * @param function the combining function. - * @param the type of the composed {@link CompletionStage}. - * @param the type of the first stage's value. - * @param the type of the second stage's value. - * @param the type of the third stage's value. - * @param the type of the fourth stage's value. - * @param the type of the fifth stage's value. - * @param the type of the sixth stage's value. + * @param the type of the composed {@link CompletionStage}. + * @param the type of the first stage's value. + * @param the type of the second stage's value. + * @param the type of the third stage's value. + * @param the type of the fourth stage's value. + * @param the type of the fifth stage's value. + * @param the type of the sixth stage's value. * @return a stage that is composed from the input stages using the function. - * @throws UnsupportedOperationException if any of the {@link CompletionStage}s - * do not interoperate with CompletableFuture + * @throws UnsupportedOperationException if any of the {@link CompletionStage}s do not + * interoperate with CompletableFuture */ public static CompletionStage combineFutures( CompletionStage a, @@ -713,33 +742,30 @@ public static CompletionStage combineFutures( final CompletableFuture ff = f.toCompletableFuture(); return CompletableFuture.allOf(af, bf, cf, df, ef, ff) - .thenCompose(ignored -> function.apply(af.join(), - bf.join(), - cf.join(), - df.join(), - ef.join(), - ff.join())); + .thenCompose( + ignored -> + function.apply(af.join(), bf.join(), cf.join(), df.join(), ef.join(), ff.join())); } /** * Polls an external resource periodically until it returns a non-empty result. * - *

The polling task should return {@code Optional.empty()} until it becomes available, and - * then {@code Optional.of(result)}. If the polling task throws an exception or returns null, - * that will cause the result future to complete exceptionally. + *

The polling task should return {@code Optional.empty()} until it becomes available, and then + * {@code Optional.of(result)}. If the polling task throws an exception or returns null, that will + * cause the result future to complete exceptionally. * - *

Canceling the returned future will cancel the scheduled polling task as well. + *

Canceling the returned future will cancel the scheduled polling task as well. * - *

Note that on a ScheduledThreadPoolExecutor the polling task might remain allocated for up - * to {@code frequency} time after completing or being cancelled. If you have lots of polling + *

Note that on a ScheduledThreadPoolExecutor the polling task might remain allocated for up to + * {@code frequency} time after completing or being cancelled. If you have lots of polling * operations or a long polling frequency, consider setting {@code removeOnCancelPolicy} to true. * See {@link java.util.concurrent.ScheduledThreadPoolExecutor#setRemoveOnCancelPolicy(boolean)}. * - * @param pollingTask the polling task - * @param frequency the frequency to run the polling task at + * @param pollingTask the polling task + * @param frequency the frequency to run the polling task at * @param executorService the executor service to schedule the polling task on - * @param the type of the result of the polling task, that will be returned when - * the task succeeds. + * @param the type of the result of the polling task, that will be returned when the task + * succeeds. * @return a future completing to the result of the polling task once that becomes available */ public static CompletableFuture poll( @@ -747,20 +773,19 @@ public static CompletableFuture poll( final Duration frequency, final ScheduledExecutorService executorService) { final CompletableFuture result = new CompletableFuture<>(); - final ScheduledFuture scheduled = executorService.scheduleAtFixedRate( - () -> pollTask(pollingTask, result), 0, frequency.toMillis(), TimeUnit.MILLISECONDS); + final ScheduledFuture scheduled = + executorService.scheduleAtFixedRate( + () -> pollTask(pollingTask, result), 0, frequency.toMillis(), TimeUnit.MILLISECONDS); result.whenComplete((r, ex) -> scheduled.cancel(true)); return result; } private static void pollTask( - final Supplier> pollingTask, - final CompletableFuture resultFuture) { + final Supplier> pollingTask, final CompletableFuture resultFuture) { try { pollingTask.get().ifPresent(resultFuture::complete); } catch (Exception ex) { resultFuture.completeExceptionally(ex); } } - } diff --git a/src/test/java/com/spotify/futures/CompletableFuturesTest.java b/src/test/java/com/spotify/futures/CompletableFuturesTest.java index 23bf97f..100dbe2 100644 --- a/src/test/java/com/spotify/futures/CompletableFuturesTest.java +++ b/src/test/java/com/spotify/futures/CompletableFuturesTest.java @@ -514,6 +514,33 @@ public void exceptionallyCompose_returnsNull() { assertThat(e.getCause(), is(instanceOf(NullPointerException.class))); } + @Test + public void supplyAsyncCompose_completed() { + final CompletionStage future = completedFuture("hello"); + final CompletionStage composed = CompletableFutures.supplyAsyncCompose(() -> future); + + assertThat(composed, completesTo("hello")); + } + + @Test + public void supplyAsyncCompose_chain_completed() { + final CompletionStage future = completedFuture("hello").thenApply(previous -> previous + "-chained"); + final CompletionStage composed = CompletableFutures.supplyAsyncCompose(() -> future); + + assertThat(composed, completesTo("hello-chained")); + } + + @Test + public void supplyAsyncCompose_failure() { + final IllegalStateException ex = new IllegalStateException(); + final CompletionStage future = exceptionallyCompletedFuture(ex); + final CompletionStage composed = CompletableFutures.supplyAsyncCompose(() -> future); + + final CompletionException e = + assertThrows(CompletionException.class, () -> getCompleted(composed)); + assertThat(e.getCause(), is(equalTo(ex))); + } + @Test public void handleCompose_completed() { final CompletionStage future = exceptionallyCompletedFuture(new Exception("boom")); From 57e4fa3871f16b123fda55424b76906bc7fecc73 Mon Sep 17 00:00:00 2001 From: Thim Lohse Date: Mon, 27 Nov 2023 11:54:01 +0100 Subject: [PATCH 2/4] update README --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 8e28133..e0cc73f 100644 --- a/README.md +++ b/README.md @@ -182,6 +182,16 @@ CompletionStage> wrapped = completedFuture(completedFutu CompletionStage unwrapped = CompletableFutures.dereference(wrapped); ``` +#### supplyAsyncCompose + +Like `CompletableFuture.supplyAsync` but unwraps a `CompletionStage>` to a plain `CompletionStage` when +the `Supplier` returns a `CompletionStage`. + +```java +CompletionStage suppliedStage = completedFuture("hello").thenApply(stage -> stage + "-chained"); +CompletionStage outputStage = CompletableFutures.supplyAsyncCompose(suppliedStage); +``` + #### exceptionallyCompletedFuture Creates a new future that is already exceptionally completed with the given exception. From 6344c52674146370de5c0ef62ca0ada7b9a94ee5 Mon Sep 17 00:00:00 2001 From: Thim Lohse Date: Mon, 27 Nov 2023 12:12:16 +0100 Subject: [PATCH 3/4] avoid formatting --- .../spotify/futures/CompletableFutures.java | 474 +++++++++--------- 1 file changed, 242 insertions(+), 232 deletions(-) diff --git a/src/main/java/com/spotify/futures/CompletableFutures.java b/src/main/java/com/spotify/futures/CompletableFutures.java index e3d8c37..15dd515 100644 --- a/src/main/java/com/spotify/futures/CompletableFutures.java +++ b/src/main/java/com/spotify/futures/CompletableFutures.java @@ -7,9 +7,9 @@ * 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. @@ -44,8 +44,8 @@ import java.util.stream.Collector; /** - * A collection of static utility methods that extend the {@link - * java.util.concurrent.CompletableFuture Java completable future} API. + * A collection of static utility methods that extend the + * {@link java.util.concurrent.CompletableFuture Java completable future} API. * * @since 0.1.0 */ @@ -57,16 +57,16 @@ private CompletableFutures() { /** * Returns a new {@link CompletableFuture} which completes to a list of all values of its input - * stages, if all succeed. The list of results is in the same order as the input stages. + * stages, if all succeed. The list of results is in the same order as the input stages. * - *

As soon as any of the given stages complete exceptionally, then the returned future also - * does so, with a {@link CompletionException} holding this exception as its cause. + *

As soon as any of the given stages complete exceptionally, then the returned future also does so, + * with a {@link CompletionException} holding this exception as its cause. * - *

If no stages are provided, returns a future holding an empty list. + *

If no stages are provided, returns a future holding an empty list. * * @param stages the stages to combine - * @param the common super-type of all of the input stages, that determines the monomorphic - * type of the output future + * @param the common super-type of all of the input stages, that determines the monomorphic + * type of the output future * @return a future that completes to a list of the results of the supplied stages * @throws NullPointerException if the stages list or any of its elements are {@code null} * @since 0.1.0 @@ -85,17 +85,16 @@ public static CompletableFuture> allAsList( CompletableFuture allOf = CompletableFuture.allOf(all); for (int i = 0; i < all.length; i++) { - all[i].exceptionally( - throwable -> { - if (!allOf.isDone()) { - allOf.completeExceptionally(throwable); - } - return null; // intentionally unused - }); + all[i].exceptionally(throwable -> { + if (!allOf.isDone()) { + allOf.completeExceptionally(throwable); + } + return null; // intentionally unused + }); } - return allOf.thenApply( - ignored -> { + return allOf + .thenApply(ignored -> { final List result = new ArrayList<>(all.length); for (int i = 0; i < all.length; i++) { result.add(all[i].join()); @@ -108,15 +107,15 @@ public static CompletableFuture> allAsList( * Returns a new {@link CompletableFuture} which completes to a map of all values of its input * stages, if all succeed. * - *

If any of the given stages complete exceptionally, then the returned future also does so, + *

If any of the given stages complete exceptionally, then the returned future also does so, * with a {@link CompletionException} holding this exception as its cause. * - *

If no stages are provided, returns a future holding an empty map. + *

If no stages are provided, returns a future holding an empty map. * * @param map the map of stages to combine - * @param the common super-type of the keys - * @param the common super-type of all of the input value-stages, that determines the - * monomorphic type of the output future + * @param the common super-type of the keys + * @param the common super-type of all of the input value-stages, that determines the + * monomorphic type of the output future * @return a future that completes to a map of the results of the supplied keys and value-stages * @throws NullPointerException if value-stages or any of its elements are {@code null} * @since 0.3.3 @@ -151,25 +150,28 @@ public static CompletableFuture> allAsMap( * * @param stages the stages to combine. * @param defaultValueMapper a function that will be called when a future completes exceptionally - * to provide a default value to place in the resulting list - * @param the common type of all of the input stages, that determines the type of the output - * future + * to provide a default value to place in the resulting list + * @param the common type of all of the input stages, that determines the type of the + * output future * @return a future that completes to a list of the results of the supplied stages * @throws NullPointerException if the stages list or any of its elements are {@code null} */ public static CompletableFuture> successfulAsList( List> stages, Function defaultValueMapper) { - return stages.stream().map(f -> f.exceptionally(defaultValueMapper)).collect(joinList()); + return stages.stream() + .map(f -> f.exceptionally(defaultValueMapper)) + .collect(joinList()); } /** - * Returns a new {@code CompletableFuture} that is already exceptionally completed with the given - * exception. + * Returns a new {@code CompletableFuture} that is already exceptionally completed with + * the given exception. * * @param throwable the exception - * @param an arbitrary type for the returned future; can be anything since the future will be - * exceptionally completed and thus there will never be a value of type {@code T} + * @param an arbitrary type for the returned future; can be anything since the future + * will be exceptionally completed and thus there will never be a value of type + * {@code T} * @return a future that exceptionally completed with the supplied exception * @throws NullPointerException if the supplied throwable is {@code null} * @since 0.1.0 @@ -181,10 +183,10 @@ public static CompletableFuture exceptionallyCompletedFuture(Throwable th } /** - * Collect a stream of {@link CompletionStage}s into a single future holding a list of the joined - * entities. + * Collect a stream of {@link CompletionStage}s into a single future holding a list of the + * joined entities. * - *

Usage: + *

Usage: * *

{@code
    * collection.stream()
@@ -193,22 +195,22 @@ public static  CompletableFuture exceptionallyCompletedFuture(Throwable th
    *     .thenApply(this::consumeList)
    * }
* - *

The generated {@link CompletableFuture} will complete to a list of all entities, in the - * order they were encountered in the original stream. Similar to {@link - * CompletableFuture#allOf(CompletableFuture[])}, if any of the input futures complete - * exceptionally, then the returned CompletableFuture also does so, with a {@link - * CompletionException} holding this exception as its cause. + *

The generated {@link CompletableFuture} will complete to a list of all entities, in the + * order they were encountered in the original stream. Similar to + * {@link CompletableFuture#allOf(CompletableFuture[])}, if any of the input futures complete + * exceptionally, then the returned CompletableFuture also does so, with a + * {@link CompletionException} holding this exception as its cause. * * @param the common super-type of all of the input stages, that determines the monomorphic - * type of the output future + * type of the output future * @param the implementation of {@link CompletionStage} that the stream contains * @return a new {@link CompletableFuture} according to the rules outlined in the method - * description + * description * @throws NullPointerException if any future in the stream is {@code null} * @since 0.1.0 */ public static > - Collector>> joinList() { + Collector>> joinList() { return collectingAndThen(toList(), CompletableFutures::allAsList); } @@ -217,7 +219,7 @@ public static CompletableFuture exceptionallyCompletedFuture(Throwable th * applying the provided mapping function to the input elements, and whose values are the * corresponding joined entities. * - *

Usage: + *

Usage: * *

{@code
    * collection.stream()
@@ -225,27 +227,27 @@ public static  CompletableFuture exceptionallyCompletedFuture(Throwable th
    *     .thenApply(this::consumeMap)
    * }
* - *

The generated {@link CompletableFuture} will complete to a Map of all entities. Similar to - * {@link CompletableFuture#allOf(CompletableFuture[])}, if any of the input futures complete - * exceptionally, then the returned CompletableFuture also does so, with a {@link - * CompletionException} holding this exception as its cause. + *

The generated {@link CompletableFuture} will complete to a Map of all entities. Similar + * to {@link CompletableFuture#allOf(CompletableFuture[])}, if any of the input futures + * complete exceptionally, then the returned CompletableFuture also does so, with a + * {@link CompletionException} holding this exception as its cause. * * @param keyMapper a mapping function to produce keys - * @param valueFutureMapper a mapping function to produce futures that will eventually produce the - * values + * @param valueFutureMapper a mapping function to produce futures that will eventually produce + * the values * @param the type of the input elements * @param the output type of the key mapping function * @param the common super-type of the stages returned by the value future mapping function * @return a new {@link CompletableFuture} according to the rules outlined in the method - * description + * description * @throws NullPointerException if valueFutureMapper returns {@code null} for any input element * @since 0.3.4 */ public static Collector>> joinMap( - Function keyMapper, - Function> valueFutureMapper) { + Function keyMapper, + Function> valueFutureMapper){ Collector>> collector = - toMap(keyMapper, valueFutureMapper); + toMap(keyMapper, valueFutureMapper); return collectingAndThen(collector, CompletableFutures::allAsMap); } @@ -266,7 +268,7 @@ public static void checkCompleted(CompletionStage stage) { * Gets the value of a completed stage. * * @param stage a completed {@link CompletionStage} - * @param the type of the value that the stage completes into + * @param the type of the value that the stage completes into * @return the value of the stage if it has one * @throws IllegalStateException if the stage is not completed * @since 0.1.0 @@ -279,14 +281,13 @@ public static T getCompleted(CompletionStage stage) { /** * Gets the exception from an exceptionally completed future - * * @param stage an exceptionally completed {@link CompletionStage} - * @param the type of the value that the stage completes into + * @param the type of the value that the stage completes into * @return the exception the stage has completed with * @throws IllegalStateException if the stage is not completed exceptionally * @throws CancellationException if the stage was cancelled - * @throws UnsupportedOperationException if the {@link CompletionStage} does not support the - * {@link CompletionStage#toCompletableFuture()} operation + * @throws UnsupportedOperationException if the {@link CompletionStage} does not + * support the {@link CompletionStage#toCompletableFuture()} operation */ public static Throwable getException(CompletionStage stage) { CompletableFuture future = stage.toCompletableFuture(); @@ -305,46 +306,52 @@ public static Throwable getException(CompletionStage stage) { * Returns a new stage that, when this stage completes either normally or exceptionally, is * executed with this stage's result and exception as arguments to the supplied function. * - *

When this stage is complete, the given function is invoked with the result (or {@code null} + *

When this stage is complete, the given function is invoked with the result (or {@code null} * if none) and the exception (or {@code null} if none) of this stage as arguments, and the * function's result is used to complete the returned stage. * - *

This differs from {@link - * java.util.concurrent.CompletionStage#handle(java.util.function.BiFunction)} in that the + *

This differs from + * {@link java.util.concurrent.CompletionStage#handle(java.util.function.BiFunction)} in that the * function should return a {@link java.util.concurrent.CompletionStage} rather than the value * directly. * * @param stage the {@link CompletionStage} to compose - * @param fn the function to use to compute the value of the returned {@link CompletionStage} - * @param the type of the input stage's value. - * @param the function's return type + * @param fn the function to use to compute the value of the + * returned {@link CompletionStage} + * @param the type of the input stage's value. + * @param the function's return type * @return the new {@link CompletionStage} * @since 0.1.0 */ public static CompletionStage handleCompose( - CompletionStage stage, BiFunction> fn) { + CompletionStage stage, + BiFunction> fn) { return dereference(stage.handle(fn)); } /** - * Returns a new stage that, when this stage completes exceptionally, is executed with this - * stage's exception as the argument to the supplied function. Otherwise, if this stage completes - * normally, then the returned stage also completes normally with the same value. + * Returns a new stage that, when this stage completes + * exceptionally, is executed with this stage's exception as the + * argument to the supplied function. Otherwise, if this stage + * completes normally, then the returned stage also completes + * normally with the same value. * - *

This differs from {@link - * java.util.concurrent.CompletionStage#exceptionally(java.util.function.Function)} in that the - * function should return a {@link java.util.concurrent.CompletionStage} rather than the value - * directly. + *

This differs from + * {@link java.util.concurrent.CompletionStage#exceptionally(java.util.function.Function)} + * in that the function should return a {@link java.util.concurrent.CompletionStage} rather than + * the value directly. * * @param stage the {@link CompletionStage} to compose - * @param fn the function to use to compute the value of the returned {@link CompletionStage} if - * this stage completed exceptionally - * @param the type of the input stage's value. + * @param fn the function to use to compute the value of the + * returned {@link CompletionStage} if this stage completed + * exceptionally + * @param the type of the input stage's value. * @return the new {@link CompletionStage} * @since 0.1.0 */ public static CompletionStage exceptionallyCompose( - CompletionStage stage, Function> fn) { + CompletionStage stage, + Function> fn) { return dereference(wrap(stage).exceptionally(fn)); } @@ -378,7 +385,7 @@ public static CompletionStage supplyAsyncCompose(Supplier CompletionStage supplyAsyncCompose( - Supplier> supplier, Executor executor) { + Supplier> supplier, Executor executor) { return dereference(CompletableFuture.supplyAsync(supplier, executor)); } @@ -386,7 +393,7 @@ public static CompletionStage supplyAsyncCompose( * This takes a stage of a stage of a value and returns a plain stage of a value. * * @param stage a {@link CompletionStage} of a {@link CompletionStage} of a value - * @param the type of the inner stage's value. + * @param the type of the inner stage's value. * @return the {@link CompletionStage} of the value * @since 0.1.0 */ @@ -403,38 +410,37 @@ private static CompletionStage> wrap(CompletionStage f /** * Combines multiple stages by applying a function. * - * @param a the first stage. - * @param b the second stage. + * @param a the first stage. + * @param b the second stage. * @param function the combining function. - * @param the type of the combining function's return value. - * @param the type of the first stage's value. - * @param the type of the second stage's value. + * @param the type of the combining function's return value. + * @param the type of the first stage's value. + * @param the type of the second stage's value. * @return a stage that completes into the return value of the supplied function. * @since 0.1.0 */ public static CompletionStage combine( - CompletionStage a, CompletionStage b, BiFunction function) { + CompletionStage a, CompletionStage b, + BiFunction function) { return a.thenCombine(b, function); } /** * Combines multiple stages by applying a function. * - * @param a the first stage. - * @param b the second stage. - * @param c the third stage. + * @param a the first stage. + * @param b the second stage. + * @param c the third stage. * @param function the combining function. - * @param the type of the combining function's return value. - * @param the type of the first stage's value. - * @param the type of the second stage's value. - * @param the type of the third stage's value. + * @param the type of the combining function's return value. + * @param the type of the first stage's value. + * @param the type of the second stage's value. + * @param the type of the third stage's value. * @return a stage that completes into the return value of the supplied function. * @since 0.1.0 */ public static CompletionStage combine( - CompletionStage a, - CompletionStage b, - CompletionStage c, + CompletionStage a, CompletionStage b, CompletionStage c, Function3 function) { final CompletableFuture af = a.toCompletableFuture(); final CompletableFuture bf = b.toCompletableFuture(); @@ -447,24 +453,21 @@ public static CompletionStage combine( /** * Combines multiple stages by applying a function. * - * @param a the first stage. - * @param b the second stage. - * @param c the third stage. - * @param d the fourth stage. + * @param a the first stage. + * @param b the second stage. + * @param c the third stage. + * @param d the fourth stage. * @param function the combining function. - * @param the type of the combining function's return value. - * @param the type of the first stage's value. - * @param the type of the second stage's value. - * @param the type of the third stage's value. - * @param the type of the fourth stage's value. + * @param the type of the combining function's return value. + * @param the type of the first stage's value. + * @param the type of the second stage's value. + * @param the type of the third stage's value. + * @param the type of the fourth stage's value. * @return a stage that completes into the return value of the supplied function. * @since 0.1.0 */ public static CompletionStage combine( - CompletionStage a, - CompletionStage b, - CompletionStage c, - CompletionStage d, + CompletionStage a, CompletionStage b, CompletionStage c, CompletionStage d, Function4 function) { final CompletableFuture af = a.toCompletableFuture(); final CompletableFuture bf = b.toCompletableFuture(); @@ -478,27 +481,24 @@ public static CompletionStage combine( /** * Combines multiple stages by applying a function. * - * @param a the first stage. - * @param b the second stage. - * @param c the third stage. - * @param d the fourth stage. - * @param e the fifth stage. + * @param a the first stage. + * @param b the second stage. + * @param c the third stage. + * @param d the fourth stage. + * @param e the fifth stage. * @param function the combining function. - * @param the type of the combining function's return value. - * @param the type of the first stage's value. - * @param the type of the second stage's value. - * @param the type of the third stage's value. - * @param the type of the fourth stage's value. - * @param the type of the fifth stage's value. + * @param the type of the combining function's return value. + * @param the type of the first stage's value. + * @param the type of the second stage's value. + * @param the type of the third stage's value. + * @param the type of the fourth stage's value. + * @param the type of the fifth stage's value. * @return a stage that completes into the return value of the supplied function. * @since 0.1.0 */ public static CompletionStage combine( - CompletionStage a, - CompletionStage b, - CompletionStage c, - CompletionStage d, - CompletionStage e, + CompletionStage a, CompletionStage b, CompletionStage c, + CompletionStage d, CompletionStage e, Function5 function) { final CompletableFuture af = a.toCompletableFuture(); final CompletableFuture bf = b.toCompletableFuture(); @@ -507,37 +507,33 @@ public static CompletionStage combine( final CompletableFuture ef = e.toCompletableFuture(); return CompletableFuture.allOf(af, bf, cf, df, ef) - .thenApply( - ignored -> function.apply(af.join(), bf.join(), cf.join(), df.join(), ef.join())); + .thenApply(ignored -> + function.apply(af.join(), bf.join(), cf.join(), df.join(), ef.join())); } /** * Combines multiple stages by applying a function. * - * @param a the first stage. - * @param b the second stage. - * @param c the third stage. - * @param d the fourth stage. - * @param e the fifth stage. - * @param f the sixth stage. + * @param a the first stage. + * @param b the second stage. + * @param c the third stage. + * @param d the fourth stage. + * @param e the fifth stage. + * @param f the sixth stage. * @param function the combining function. - * @param the type of the combining function's return value. - * @param the type of the first stage's value. - * @param the type of the second stage's value. - * @param the type of the third stage's value. - * @param the type of the fourth stage's value. - * @param the type of the fifth stage's value. - * @param the type of the sixth stage's value. + * @param the type of the combining function's return value. + * @param the type of the first stage's value. + * @param the type of the second stage's value. + * @param the type of the third stage's value. + * @param the type of the fourth stage's value. + * @param the type of the fifth stage's value. + * @param the type of the sixth stage's value. * @return a stage that completes into the return value of the supplied function. * @since 0.3.2 */ public static CompletionStage combine( - CompletionStage a, - CompletionStage b, - CompletionStage c, - CompletionStage d, - CompletionStage e, - CompletionStage f, + CompletionStage a, CompletionStage b, CompletionStage c, + CompletionStage d, CompletionStage e, CompletionStage f, Function6 function) { final CompletableFuture af = a.toCompletableFuture(); final CompletableFuture bf = b.toCompletableFuture(); @@ -547,22 +543,26 @@ public static CompletionStage combine( final CompletableFuture ff = f.toCompletableFuture(); return CompletableFuture.allOf(af, bf, cf, df, ef, ff) - .thenApply( - ignored -> - function.apply(af.join(), bf.join(), cf.join(), df.join(), ef.join(), ff.join())); + .thenApply(ignored -> + function.apply(af.join(), + bf.join(), + cf.join(), + df.join(), + ef.join(), + ff.join())); } /** * Combines multiple stages by applying a function. * * @param function the combining function. - * @param stages the stages to combine - * @param the type of the combining function's return value. + * @param stages the stages to combine + * @param the type of the combining function's return value. * @return a stage that completes into the return value of the supplied function. * @since 0.4.0 */ public static CompletionStage combine( - Function function, CompletionStage... stages) { + Function function, CompletionStage... stages) { return combine(function, Arrays.asList(stages)); } @@ -570,37 +570,38 @@ public static CompletionStage combine( * Combines multiple stages by applying a function. * * @param function the combining function. - * @param stages the stages to combine - * @param the type of the combining function's return value. + * @param stages the stages to combine + * @param the type of the combining function's return value. * @return a stage that completes into the return value of the supplied function. * @since 0.4.0 */ public static CompletionStage combine( - Function function, List> stages) { + Function function, List> stages) { @SuppressWarnings("unchecked") // generic array creation final CompletableFuture[] all = new CompletableFuture[stages.size()]; for (int i = 0; i < stages.size(); i++) { all[i] = stages.get(i).toCompletableFuture(); } - return CompletableFuture.allOf(all) - .thenApply(ignored -> function.apply(new CombinedFutures(stages))); + return CompletableFuture.allOf(all).thenApply(ignored -> function.apply(new CombinedFutures(stages))); } /** * Composes multiple stages into another stage using a function. * - * @param a the first stage. - * @param b the second stage. + * @param a the first stage. + * @param b the second stage. * @param function the combining function. - * @param the type of the composed {@link CompletionStage}. - * @param the type of the first stage's value. - * @param the type of the second stage's value. + * @param the type of the composed {@link CompletionStage}. + * @param the type of the first stage's value. + * @param the type of the second stage's value. * @return a stage that is composed from the input stages using the function. - * @throws UnsupportedOperationException if any of the {@link CompletionStage}s do not - * interoperate with CompletableFuture + * @throws UnsupportedOperationException if any of the {@link CompletionStage}s + * do not interoperate with CompletableFuture */ public static CompletionStage combineFutures( - CompletionStage a, CompletionStage b, BiFunction> function) { + CompletionStage a, + CompletionStage b, + BiFunction> function) { final CompletableFuture af = a.toCompletableFuture(); final CompletableFuture bf = b.toCompletableFuture(); @@ -611,17 +612,17 @@ public static CompletionStage combineFutures( /** * Composes multiple stages into another stage using a function. * - * @param a the first stage. - * @param b the second stage. - * @param c the third stage. + * @param a the first stage. + * @param b the second stage. + * @param c the third stage. * @param function the combining function. - * @param the type of the composed {@link CompletionStage}. - * @param the type of the first stage's value. - * @param the type of the second stage's value. - * @param the type of the third stage's value. + * @param the type of the composed {@link CompletionStage}. + * @param the type of the first stage's value. + * @param the type of the second stage's value. + * @param the type of the third stage's value. * @return a stage that is composed from the input stages using the function. - * @throws UnsupportedOperationException if any of the {@link CompletionStage}s do not - * interoperate with CompletableFuture + * @throws UnsupportedOperationException if any of the {@link CompletionStage}s + * do not interoperate with CompletableFuture */ public static CompletionStage combineFutures( CompletionStage a, @@ -633,25 +634,27 @@ public static CompletionStage combineFutures( final CompletableFuture cf = c.toCompletableFuture(); return CompletableFuture.allOf(af, bf, cf) - .thenCompose(ignored -> function.apply(af.join(), bf.join(), cf.join())); + .thenCompose(ignored -> function.apply(af.join(), + bf.join(), + cf.join())); } /** * Composes multiple stages into another stage using a function. * - * @param a the first stage. - * @param b the second stage. - * @param c the third stage. - * @param d the fourth stage. + * @param a the first stage. + * @param b the second stage. + * @param c the third stage. + * @param d the fourth stage. * @param function the combining function. - * @param the type of the composed {@link CompletionStage}. - * @param the type of the first stage's value. - * @param the type of the second stage's value. - * @param the type of the third stage's value. - * @param the type of the fourth stage's value. + * @param the type of the composed {@link CompletionStage}. + * @param the type of the first stage's value. + * @param the type of the second stage's value. + * @param the type of the third stage's value. + * @param the type of the fourth stage's value. * @return a stage that is composed from the input stages using the function. - * @throws UnsupportedOperationException if any of the {@link CompletionStage}s do not - * interoperate with CompletableFuture + * @throws UnsupportedOperationException if any of the {@link CompletionStage}s + * do not interoperate with CompletableFuture */ public static CompletionStage combineFutures( CompletionStage a, @@ -671,21 +674,21 @@ public static CompletionStage combineFutures( /** * Composes multiple stages into another stage using a function. * - * @param a the first stage. - * @param b the second stage. - * @param c the third stage. - * @param d the fourth stage. - * @param e the fifth stage. + * @param a the first stage. + * @param b the second stage. + * @param c the third stage. + * @param d the fourth stage. + * @param e the fifth stage. * @param function the combining function. - * @param the type of the composed {@link CompletionStage}. - * @param the type of the first stage's value. - * @param the type of the second stage's value. - * @param the type of the third stage's value. - * @param the type of the fourth stage's value. - * @param the type of the fifth stage's value. + * @param the type of the composed {@link CompletionStage}. + * @param the type of the first stage's value. + * @param the type of the second stage's value. + * @param the type of the third stage's value. + * @param the type of the fourth stage's value. + * @param the type of the fifth stage's value. * @return a stage that is composed from the input stages using the function. - * @throws UnsupportedOperationException if any of the {@link CompletionStage}s do not - * interoperate with CompletableFuture + * @throws UnsupportedOperationException if any of the {@link CompletionStage}s + * do not interoperate with CompletableFuture */ public static CompletionStage combineFutures( CompletionStage a, @@ -701,30 +704,33 @@ public static CompletionStage combineFutures( final CompletableFuture ef = e.toCompletableFuture(); return CompletableFuture.allOf(af, bf, cf, df, ef) - .thenCompose( - ignored -> function.apply(af.join(), bf.join(), cf.join(), df.join(), ef.join())); + .thenCompose(ignored -> function.apply(af.join(), + bf.join(), + cf.join(), + df.join(), + ef.join())); } /** * Composes multiple stages into another stage using a function. * - * @param a the first stage. - * @param b the second stage. - * @param c the third stage. - * @param d the fourth stage. - * @param e the fifth stage. - * @param f the sixth stage. + * @param a the first stage. + * @param b the second stage. + * @param c the third stage. + * @param d the fourth stage. + * @param e the fifth stage. + * @param f the sixth stage. * @param function the combining function. - * @param the type of the composed {@link CompletionStage}. - * @param the type of the first stage's value. - * @param the type of the second stage's value. - * @param the type of the third stage's value. - * @param the type of the fourth stage's value. - * @param the type of the fifth stage's value. - * @param the type of the sixth stage's value. + * @param the type of the composed {@link CompletionStage}. + * @param the type of the first stage's value. + * @param the type of the second stage's value. + * @param the type of the third stage's value. + * @param the type of the fourth stage's value. + * @param the type of the fifth stage's value. + * @param the type of the sixth stage's value. * @return a stage that is composed from the input stages using the function. - * @throws UnsupportedOperationException if any of the {@link CompletionStage}s do not - * interoperate with CompletableFuture + * @throws UnsupportedOperationException if any of the {@link CompletionStage}s + * do not interoperate with CompletableFuture */ public static CompletionStage combineFutures( CompletionStage a, @@ -742,30 +748,33 @@ public static CompletionStage combineFutures( final CompletableFuture ff = f.toCompletableFuture(); return CompletableFuture.allOf(af, bf, cf, df, ef, ff) - .thenCompose( - ignored -> - function.apply(af.join(), bf.join(), cf.join(), df.join(), ef.join(), ff.join())); + .thenCompose(ignored -> function.apply(af.join(), + bf.join(), + cf.join(), + df.join(), + ef.join(), + ff.join())); } /** * Polls an external resource periodically until it returns a non-empty result. * - *

The polling task should return {@code Optional.empty()} until it becomes available, and then - * {@code Optional.of(result)}. If the polling task throws an exception or returns null, that will - * cause the result future to complete exceptionally. + *

The polling task should return {@code Optional.empty()} until it becomes available, and + * then {@code Optional.of(result)}. If the polling task throws an exception or returns null, + * that will cause the result future to complete exceptionally. * - *

Canceling the returned future will cancel the scheduled polling task as well. + *

Canceling the returned future will cancel the scheduled polling task as well. * - *

Note that on a ScheduledThreadPoolExecutor the polling task might remain allocated for up to - * {@code frequency} time after completing or being cancelled. If you have lots of polling + *

Note that on a ScheduledThreadPoolExecutor the polling task might remain allocated for up + * to {@code frequency} time after completing or being cancelled. If you have lots of polling * operations or a long polling frequency, consider setting {@code removeOnCancelPolicy} to true. * See {@link java.util.concurrent.ScheduledThreadPoolExecutor#setRemoveOnCancelPolicy(boolean)}. * - * @param pollingTask the polling task - * @param frequency the frequency to run the polling task at + * @param pollingTask the polling task + * @param frequency the frequency to run the polling task at * @param executorService the executor service to schedule the polling task on - * @param the type of the result of the polling task, that will be returned when the task - * succeeds. + * @param the type of the result of the polling task, that will be returned when + * the task succeeds. * @return a future completing to the result of the polling task once that becomes available */ public static CompletableFuture poll( @@ -773,19 +782,20 @@ public static CompletableFuture poll( final Duration frequency, final ScheduledExecutorService executorService) { final CompletableFuture result = new CompletableFuture<>(); - final ScheduledFuture scheduled = - executorService.scheduleAtFixedRate( - () -> pollTask(pollingTask, result), 0, frequency.toMillis(), TimeUnit.MILLISECONDS); + final ScheduledFuture scheduled = executorService.scheduleAtFixedRate( + () -> pollTask(pollingTask, result), 0, frequency.toMillis(), TimeUnit.MILLISECONDS); result.whenComplete((r, ex) -> scheduled.cancel(true)); return result; } private static void pollTask( - final Supplier> pollingTask, final CompletableFuture resultFuture) { + final Supplier> pollingTask, + final CompletableFuture resultFuture) { try { pollingTask.get().ifPresent(resultFuture::complete); } catch (Exception ex) { resultFuture.completeExceptionally(ex); } } + } From 34611655114ca7fa8e3d07da2d37310cbda8a7ad Mon Sep 17 00:00:00 2001 From: Thim Lohse Date: Mon, 27 Nov 2023 12:15:24 +0100 Subject: [PATCH 4/4] add since next release --- src/main/java/com/spotify/futures/CompletableFutures.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/spotify/futures/CompletableFutures.java b/src/main/java/com/spotify/futures/CompletableFutures.java index 15dd515..b6b0f99 100644 --- a/src/main/java/com/spotify/futures/CompletableFutures.java +++ b/src/main/java/com/spotify/futures/CompletableFutures.java @@ -366,6 +366,7 @@ public static CompletionStage exceptionallyCompose( * @param supplier a {@link Supplier} with a return value of {@link CompletionStage} * @param the type of the supplied stage's value * @return the new {@link CompletionStage} + * @since 0.3.6 */ public static CompletionStage supplyAsyncCompose(Supplier> supplier) { return dereference(CompletableFuture.supplyAsync(supplier)); @@ -383,6 +384,7 @@ public static CompletionStage supplyAsyncCompose(Supplier the type of the supplied stage's value * @return the new {@link CompletionStage} + * @since 0.3.6 */ public static CompletionStage supplyAsyncCompose( Supplier> supplier, Executor executor) {