001
002package io.vrap.rmf.base.client.utils;
003
004import static java.util.stream.Collectors.toList;
005
006import java.util.List;
007import java.util.NoSuchElementException;
008import java.util.concurrent.*;
009import java.util.function.BiConsumer;
010import java.util.function.Consumer;
011import java.util.function.Function;
012import java.util.function.Supplier;
013import java.util.stream.Collectors;
014
015/**
016 * Tools to simplify the work with {@link CompletionStage} and {@link CompletableFuture}.
017 */
018public final class CompletableFutureUtils {
019    private CompletableFutureUtils() {
020    }
021
022    /**
023     * <p>Creates a {@link CompletableFuture} which is completed successfully with the given object.</p>
024     *
025     * {@include.example io.vrap.rmf.base.client.utils.CompletableFutureUtilsTest#testSuccessful()}
026     *
027     * @param object the result of the future
028     * @param <T> the type of the object
029     * @return future
030     */
031    public static <T> CompletableFuture<T> successful(final T object) {
032        return CompletableFuture.completedFuture(object);
033    }
034
035    static <T> Throwable blockForFailure(final CompletionStage<T> future) {
036        try {
037            future.toCompletableFuture().join();
038            throw new NoSuchElementException(future + " did not complete exceptionally.");
039        }
040        catch (final CompletionException e1) {
041            return e1.getCause();
042        }
043    }
044
045    /**
046     * <p>Creates a {@link CompletableFuture} which is completed exceptionally with the given Exception.
047     * Alias of {@link #failed(Throwable)}.</p>
048     *
049     * {@include.example io.vrap.rmf.base.client.utils.CompletableFutureUtilsTest#testFailed()}
050     *
051     * @param e exception for the future
052     * @param <T> the type of the value of the success case
053     * @return future
054     */
055    public static <T> CompletableFuture<T> exceptionallyCompletedFuture(final Throwable e) {
056        return failed(e);
057    }
058
059    /**
060     * <p>Creates a {@link CompletableFuture} which is completed exceptionally with the given Exception.</p>
061     *
062     * {@include.example io.vrap.rmf.base.client.utils.CompletableFutureUtilsTest#testFailed()}
063     *
064     * @param e exception for the future
065     * @param <T> the type of the value of the success case
066     * @return future
067     */
068    public static <T> CompletableFuture<T> failed(final Throwable e) {
069        final CompletableFuture<T> future = new CompletableFuture<>();
070        future.completeExceptionally(e);
071        return future;
072    }
073
074    /**
075     * Internal JVM SDK util.
076     *
077     * @param source the stage which may be completed at some time
078     * @param target future which will receive the results of source
079     * @param <T> type of the value of the future
080     */
081    public static <T> void transferResult(final CompletionStage<T> source, final CompletableFuture<T> target) {
082        source.whenCompleteAsync((result, throwable) -> {
083            final boolean isSuccessful = throwable == null;
084            if (isSuccessful) {
085                target.complete(result);
086            }
087            else {
088                target.completeExceptionally(throwable);
089            }
090        });
091    }
092
093    /**
094     * <p>Executes a side-effect when the future completes exceptionally.</p>
095     *
096     * {@include.example io.vrap.rmf.base.client.utils.CompletableFutureUtilsTest#onFailure()}
097     *
098     * @param future the observed future
099     * @param consumer code which should be executed if the future completes exceptionally
100     * @param <T> type of the futures value
101     * @return stage which is completed when the consumer is done or the future completed successfully
102     */
103    public static <T> CompletionStage<T> onFailure(final CompletionStage<T> future,
104            final Consumer<? super Throwable> consumer) {
105        return future.whenCompleteAsync((value, throwable) -> {
106            if (throwable != null) {
107                consumer.accept(throwable);
108            }
109        });
110    }
111
112    /**
113     * <p>Executes a side-effect when the future completes successfully.</p>
114     *
115     * {@include.example io.vrap.rmf.base.client.utils.CompletableFutureUtilsTest#onSuccess()}
116     *
117     * @param future the observed future
118     * @param consumer code which should be executed if the future completes successfully
119     * @param <T> type of the futures value
120     * @return stage which is completed when the consumer is done or the future completed exceptionally
121     */
122    public static <T> CompletionStage<T> onSuccess(final CompletionStage<T> future,
123            final Consumer<? super T> consumer) {
124        return future.whenCompleteAsync((value, throwable) -> {
125            if (throwable == null) {
126                consumer.accept(value);
127            }
128        });
129    }
130
131    /**
132     * <p>Creates a {@link CompletionStage} which can be recovered if an error occurs. Alias for {@link CompletionStage#exceptionally(Function)}.
133     * If the recovery function also requires to create a {@link CompletionStage} then use {@link #recoverWith(CompletionStage, Function)}.</p>
134     *
135     * {@include.example io.vrap.rmf.base.client.utils.CompletableFutureUtilsTest#recoverFailure()}
136     *
137     * @param future future which may completes exceptionally
138     * @param f function how the exception should be handled to create a successfully completed future, but it also can throw exceptions
139     * @param <T> type of the value of the future
140     * @return a future which can be recovered from errors
141     */
142    public static <T> CompletionStage<T> recover(final CompletionStage<T> future,
143            final Function<Throwable, ? extends T> f) {
144        return future.exceptionally(f);
145    }
146
147    /**
148     * <p>Creates a {@link CompletionStage} which can be recovered if an error occurs.
149     * If the recovery function does not require to create a {@link CompletionStage} then use {@link #recover(CompletionStage, Function)}.</p>
150     *
151     * {@include.example io.vrap.rmf.base.client.utils.CompletableFutureUtilsTest#recoverWithSuccessInSecond()}
152     *
153     * @param future future which may completes exceptionally
154     * @param f function how the exception should be handled to create a successfully completed future
155     * @param <T> type of the value of the future
156     * @return a future which can be recovered from errors
157     */
158    public static <T> CompletableFuture<T> recoverWith(final CompletionStage<T> future,
159            final Function<? super Throwable, CompletionStage<T>> f) {
160        return recoverWith(future, f, ForkJoinPool.commonPool());
161    }
162
163    /**
164     * <p>Creates a {@link CompletionStage} which can be recovered if an error occurs by executing a function in a certain thread pool.
165     * If the recovery does not require to create a {@link CompletionStage} then use {@link #recover(CompletionStage, Function)}.</p>
166     *
167     * <p>Have a look at {@link #recoverWith(CompletionStage, Function)} for an example without the thread pool.</p>
168     *
169     * @param future future which may completes exceptionally
170     * @param f function how the exception should be handled to create a successfully completed future
171     * @param executor thread pool to execute the recover function
172     * @param <T> type of the value of the future
173     * @return a future which may recovered from errors
174     */
175    public static <T> CompletableFuture<T> recoverWith(final CompletionStage<T> future,
176            final Function<? super Throwable, CompletionStage<T>> f, final Executor executor) {
177        final CompletableFuture<T> result = new CompletableFuture<>();
178        final BiConsumer<T, Throwable> action = (value, error) -> {
179            if (value != null) {
180                result.complete(value);
181            }
182            else {
183                final CompletionStage<T> alternative = f.apply(error);
184                alternative.whenCompleteAsync((alternativeValue, alternativeError) -> {
185                    if (alternativeValue != null) {
186                        result.complete(alternativeValue);
187                    }
188                    else {
189                        result.completeExceptionally(alternativeError);
190                    }
191                }, executor);
192            }
193        };
194        future.whenCompleteAsync(action, executor);
195        return result;
196    }
197
198    /**
199     *
200     * <p>Tries to access the completed future if available and returns its value (or exception in case the future completed exceptionally), otherwise throws the given exception.</p>
201     *
202     * {@include.example io.vrap.rmf.base.client.utils.CompletableFutureUtilsTest#orElseThrow()}
203     *
204     * @param stage the future
205     * @param exceptionSupplier code which should be executed if the future is not yet completed (either successfully or exceptionally)
206     * @param <T> the type of the future value
207     * @param <X> the type of the exception to be thrown if the value is absent
208     * @return value
209     * @throws X exception in case the value is not available
210     */
211    public static <T, X extends Throwable> T orElseThrow(final CompletionStage<T> stage,
212            Supplier<? extends X> exceptionSupplier) throws X {
213        final CompletableFuture<T> future = stage.toCompletableFuture();
214        if (future.isDone()) {
215            return future.join();
216        }
217        else {
218            throw exceptionSupplier.get();
219        }
220    }
221
222    /**
223     *
224     * <p>Tries to access the completed future if available and returns its value (or exception in case the future completed exceptionally), otherwise uses the supplier to get a default value.</p>
225     *
226     * {@include.example io.vrap.rmf.base.client.utils.CompletableFutureUtilsTest#orElseGet()}
227     *
228     * @param stage the future
229     * @param other code which should be executed when the value is not available yet or the future completed exceptionally
230     * @param <T> the type of the future value
231     * @return value
232     */
233    public static <T> T orElseGet(final CompletionStage<T> stage, final Supplier<T> other) {
234        final CompletableFuture<T> future = stage.toCompletableFuture();
235        return future.isDone() ? future.join() : other.get();
236    }
237
238    /**
239     * <p>Applies a function to the successful result of a future.
240     * If the function needs to return a {@link CompletionStage} use {@link #flatMap(CompletionStage, Function)} instead.</p>
241     *
242     * {@include.example io.vrap.rmf.base.client.utils.CompletableFutureUtilsTest#testMap()}
243     *
244     * @param future the future to map
245     * @param f function which should be applied if the future completes successfully
246     * @param <T> type of value of the future
247     * @param <U> type of the value of the returned future which is also the return type of {@code f}
248     * @return a new future which contains either the exception from the original future or as value the result of application of {@code f} to the value of the original future.
249     */
250    public static <T, U> CompletionStage<U> map(final CompletionStage<T> future,
251            final Function<? super T, ? extends U> f) {
252        return future.thenApplyAsync(f);
253    }
254
255    /**
256     * <p>Applies a function to the successful result of a future.
257     * If the function does not to return a {@link CompletionStage} use {@link #map(CompletionStage, Function)} instead.</p>
258     *
259     * {@include.example io.vrap.rmf.base.client.utils.CompletableFutureUtilsTest#testFlatMap()}
260     *
261     * @param future the future to map
262     * @param f function which should be applied if the future completes successfully
263     * @param <T> type of value of the future
264     * @param <U> type of the value of the returned future which is also the return type of {@code f}
265     * @return a new future which contains either the exception from the original future or as value the result of application of {@code f} to the value of the original future.
266     */
267    public static <T, U> CompletionStage<U> flatMap(final CompletionStage<T> future,
268            final Function<? super T, CompletionStage<U>> f) {
269        return future.thenComposeAsync(f);
270    }
271
272    /**
273     * <p>Transforms a list of {@code CompletionStage} into a {@code CompletionStage} of a list,
274     * that will be completed once all the elements of the given list are completed.
275     * In case multiple stages end exceptionally only one error is kept.</p>
276     *
277     * <p>Alias of {@link #sequence(List)}.</p>
278     *
279     * {@include.example io.vrap.rmf.base.client.utils.CompletableFutureUtilsTest#sequence()}
280     * {@include.example io.vrap.rmf.base.client.utils.CompletableFutureUtilsTest#sequenceErrorCase()}
281     *
282     * @param list list of {@code CompletionStage}
283     * @param <T> the element obtained from the list of {@code CompletionStage}
284     * @return the {@code CompletableFuture} of a list of elements
285     */
286    public static <T> CompletableFuture<List<T>> listOfFuturesToFutureOfList(
287            final List<? extends CompletionStage<T>> list) {
288        final List<CompletableFuture<T>> futureList = list.stream()
289                .map(CompletionStage::toCompletableFuture)
290                .collect(toList());
291        final CompletableFuture[] futuresAsArray = futureList.toArray(new CompletableFuture[futureList.size()]);
292        return CompletableFuture.allOf(futuresAsArray)
293                .thenApplyAsync(x -> futureList.stream().map(CompletableFuture::join).collect(Collectors.toList()));
294    }
295
296    /**
297     * <p>Transforms a list of {@code CompletionStage} into a {@code CompletionStage} of a list,
298     * that will be completed once all the elements of the given list are completed.
299     * In case multiple stages end exceptionally only one error is kept.</p>
300     *
301     * <p>Alias of {@link #listOfFuturesToFutureOfList(List)}.</p>
302     *
303     * {@include.example io.vrap.rmf.base.client.utils.CompletableFutureUtilsTest#sequence()}
304     * {@include.example io.vrap.rmf.base.client.utils.CompletableFutureUtilsTest#sequenceErrorCase()}
305     *
306     * @param list list of {@code CompletionStage}
307     * @param <T> the element obtained from the list of {@code CompletionStage}
308     * @return the {@code CompletableFuture} of a list of elements
309     */
310    public static <T> CompletableFuture<List<T>> sequence(final List<? extends CompletionStage<T>> list) {
311        return listOfFuturesToFutureOfList(list);
312    }
313}