
com.commercetools.sync.commons.utils.CompletableFutureUtils Maven / Gradle / Ivy
package com.commercetools.sync.commons.utils;
import static java.util.stream.Collectors.toList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.function.Function;
import java.util.stream.Collector;
import java.util.stream.Stream;
import javax.annotation.Nonnull;
public final class CompletableFutureUtils {
/**
* Creates a Future containing a collection of value results after the mapper function is applied
* to each value in the supplied collection and completed it. The type of the returned collection
* is decided by the supplied collector.
*
* @param values collection of values to apply a mapper function that would map each to a {@link
* java.util.concurrent.CompletionStage}.
* @param mapper function to map each value to a {@link java.util.concurrent.CompletionStage}
* @param collector the collector to define the type of the collection returned.
* @param The type of the values.
* @param The type of the mapped completed values.
* @param The type of the collection returned in the future.
* @return a future containing a collection of completed stage results of the values after the
* mapper function was applied to each value in the supplied collection.
*/
@Nonnull
public static >
CompletableFuture mapValuesToFutureOfCompletedValues(
@Nonnull final Collection values,
@Nonnull final Function> mapper,
@Nonnull final Collector collector) {
return mapValuesToFutureOfCompletedValues(values.stream(), mapper, collector);
}
/**
* Creates a Future containing a collection of value results after the mapper function is applied
* to each value in the supplied stream and completed it. The type of the returned collection is
* decided by the supplied collector.
*
* @param values stream of values to apply a mapper function that would map each to a {@link
* java.util.concurrent.CompletionStage}.
* @param mapper function to map each value to a {@link java.util.concurrent.CompletionStage}
* @param collector the collector to define the type of the collection returned.
* @param The type of the values.
* @param The type of the mapping of the values.
* @param The type of the collection returned in the future.
* @return a future containing a list of completed stage results of the values after the mapper
* function was applied to each one.
*/
@Nonnull
private static >
CompletableFuture mapValuesToFutureOfCompletedValues(
@Nonnull final Stream values,
@Nonnull final Function> mapper,
@Nonnull final Collector collector) {
final List> futureList = mapValuesToFutures(values, mapper, toList());
return collectionOfFuturesToFutureOfCollection(futureList, collector);
}
/**
* Transforms a collection of {@code CompletionStage} into a {@code CompletionStage} of a
* collection, that will be completed once all the elements of the given futures are completed. In
* case multiple stages end exceptionally only one error is kept. The type of the returned
* collection is decided by the supplied collector.
*
* Note: Null futures in the collection are filtered out.
*
* @param futures collection of {@code CompletionStage}
* @param collector the collector to define the type of the collection returned.
* @param the element obtained from the set of {@code CompletionStage}
* @param The type of the collection returned in the future.
* @return the {@code CompletableFuture} of a collection of elements
*/
@Nonnull
public static >
CompletableFuture collectionOfFuturesToFutureOfCollection(
@Nonnull final Collection extends CompletionStage> futures,
@Nonnull final Collector collector) {
final List> futureList =
futures.stream()
.filter(Objects::nonNull)
.map(CompletionStage::toCompletableFuture)
.collect(toList());
final CompletableFuture[] futuresAsArray =
futureList.toArray(new CompletableFuture[futureList.size()]);
return CompletableFuture.allOf(futuresAsArray)
.thenApply(
ignoredResult ->
futureList.stream()
.map(CompletableFuture::join)
.filter(Objects::nonNull)
.collect(collector));
}
/**
* Maps a stream of values to a future collection using the supplied mapper function. The type of
* the returned collection is decided by the supplied collector.
*
* @param values stream of values to apply a mapper function that would map each to a {@link
* java.util.concurrent.CompletionStage}.
* @param mapper function to map each value to a {@link java.util.concurrent.CompletionStage}
* @param collector the collector to define the type of the collection returned.
* @param The type of the values.
* @param The type of the mapped values.
* @param The type of the collection returned.
* @return a collection of futures resulting from applying the mapper function on each value.
*/
@Nonnull
private static >> U mapValuesToFutures(
@Nonnull final Stream values,
@Nonnull final Function> mapper,
@Nonnull final Collector, ?, U> collector) {
final Stream> stageStream = StreamUtils.filterNullAndMap(values, mapper);
return toCompletableFutures(stageStream, collector);
}
/**
* Converts a stream of {@link java.util.concurrent.CompletionStage} of values of type {@code }
* into a {@link java.util.Collection} of the type of the supplied {@code collector} of {@link
* java.util.concurrent.CompletableFuture} of values of type {@code }. The type of the returned
* collection is decided by the supplied collector.
*
* Note: Null futures in the stream are filtered out.
*
* @param values stream of {@link java.util.concurrent.CompletionStage} of values of type {@code
* }
* @param collector the collector to define the type of the collection returned.
* @param the type of the results of the stages.
* @param the concrete type of the collection returned.
* @return a {@link java.util.List} of {@link java.util.concurrent.CompletableFuture} elements of
* type {@code }.
*/
@Nonnull
private static >> S toCompletableFutures(
@Nonnull final Stream> values,
@Nonnull final Collector, ?, S> collector) {
return values
.filter(Objects::nonNull)
.map(CompletionStage::toCompletableFuture)
.collect(collector);
}
private CompletableFutureUtils() {}
}