All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.yudichev.jiotty.common.lang.CompletableFutures Maven / Gradle / Ivy

package net.yudichev.jiotty.common.lang;

import com.google.common.collect.ImmutableList;
import org.slf4j.Logger;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collector;

import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.Collections.emptyList;
import static java.util.Collections.unmodifiableList;
import static java.util.stream.Collectors.toList;
import static net.yudichev.jiotty.common.lang.DelayedExecutors.delayedExecutor;

@SuppressWarnings("WeakerAccess") // it's a library
public final class CompletableFutures {
    private CompletableFutures() {
    }

    public static  CompletableFuture completable(Future future) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                return future.get();
            } catch (InterruptedException | ExecutionException e) {
                throw new RuntimeException(e);
            }
        });
    }

    @SuppressWarnings("ZeroLengthArrayAllocation") // this is what we need
    public static CompletableFuture allOf(ImmutableList> futures) {
        return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
    }

    public static  CompletableFuture completedFuture() {
        return CompletableFuture.completedFuture(null);
    }

    public static CompletableFuture delay(long millis) {
        return CompletableFuture.runAsync(() -> {}, delayedExecutor(millis));
    }

    public static  CompletableFuture failure(String message) {
        return failure(new RuntimeException(message));
    }

    public static  CompletableFuture failure(Throwable exception) {
        CompletableFuture future = new CompletableFuture<>();
        future.completeExceptionally(exception);
        return future;
    }

    public static  Collector, ?, CompletableFuture>> toFutureOfList() {
        return Collector.of(
                ImmutableList::>builder,
                ImmutableList.Builder::add,
                (builder1, builder2) ->
                        ImmutableList.>builder()
                                .addAll(builder1.build())
                                .addAll(builder2.build()),
                builder -> {
                    ImmutableList> listOfFutures = builder.build();
                    //noinspection ZeroLengthArrayAllocation
                    return CompletableFuture.allOf(listOfFutures.toArray(new CompletableFuture[0]))
                            .thenApply(ignored -> unmodifiableList(listOfFutures.stream()
                                    .map(CompletableFuture::join)
                                    // cannot use ImmutableList here, void futures typically return nulls
                                    .collect(toList())));
                }
        );
    }

    public static  Collector>> toFutureOfListChaining(Function> operation) {
        Object builderMutex = new Object();
        return Collector., CompletableFuture>>of(
                () -> new FutureChainBuilder<>(operation, builderMutex),
                FutureChainBuilder::accept,
                FutureChainBuilder::combinedWith,
                FutureChainBuilder::build
        );
    }

    public static  BiConsumer logErrorOnFailure(Logger logger, String errorMessageTemplate, Object... params) {
        return (aVoid, e) -> {
            if (e != null) {
                logger.error(String.format(errorMessageTemplate, params), e);
            }
        };
    }

    private static class FutureChainBuilder {
        private final Function> operation;
        private final Object mutex;
        private CompletableFuture> future;

        FutureChainBuilder(Function> operation, Object mutex) {
            this.operation = checkNotNull(operation);
            this.mutex = checkNotNull(mutex);
        }

        public FutureChainBuilder combinedWith(FutureChainBuilder another) {
            if (future == null) {
                return another;
            } else if (another.future == null) {
                return this;
            } else {
                FutureChainBuilder combinedBuilder = new FutureChainBuilder<>(operation, mutex);
                combinedBuilder.future = future.thenCompose(list1 -> another.future.thenApply(list2 -> {
                    synchronized (mutex) {
                        list1.addAll(list2);
                        return list1;
                    }
                }));
                return combinedBuilder;
            }
        }

        public void accept(T input) {
            if (future == null) {
                future = operation.apply(input)
                        .thenApply(result -> {
                            List list = new ArrayList<>();
                            list.add(result);
                            return list;
                        });
            } else {
                future = future.thenCompose(list ->
                        operation.apply(input)
                                .thenApply(result -> {
                                    synchronized (mutex) {
                                        list.add(result);
                                        return list;
                                    }
                                }));
            }
        }

        public CompletableFuture> build() {
            return future == null ? CompletableFuture.completedFuture(emptyList()) : future;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy