package com.github.aesteve.vertx.nubes.utils.async;
import com.github.aesteve.vertx.nubes.utils.functional.TriConsumer;
import io.vertx.core.AsyncResult;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Function;
import static com.github.aesteve.vertx.nubes.utils.async.AsyncUtils.completeOrFail;
public class MultipleFutures<T> extends SimpleFuture<T> {
private final Map<Handler<Future<T>>, Future<T>> consumers;
private static final Logger LOG = LoggerFactory.getLogger(MultipleFutures.class);
public MultipleFutures() {
consumers = new HashMap<>();
}
public MultipleFutures(Future<T> after) {
this();
join(after);
}
public MultipleFutures(Handler<AsyncResult<T>> after) {
this();
join(after);
}
public <L> MultipleFutures(Collection<L> elements, Function<L, Handler<Future<T>>> transform) {
this();
addAll(elements, transform);
}
public <L> MultipleFutures(Collection<L> elements, BiConsumer<L, Future<T>> transform) {
this();
addAll(elements, transform);
}
public <K, V> MultipleFutures(Map<K, V> elements, TriConsumer<K, V, Future<T>> transform) {
this();
addAll(elements, transform);
}
//
public MultipleFutures<T> add(Handler<Future<T>> handler) {
if (handler == null) {
return this;
}
Future<T> future = Future.future();
future.setHandler(futureHandler -> checkCallHandler());
consumers.put(handler, future);
return this;
}
public MultipleFutures<T> start() {
if (consumers.isEmpty()) {
complete();
return this;
}
consumers.forEach(Handler::handle);
return this;
}
@Override
public T result() {
return null;
}
@Override
public Throwable cause() {
Exception e = new Exception("At least one future failed");
consumers.forEach((consumer, future) -> {
if (future.cause() != null) {
LOG.error(future.cause());
if (e.getCause() == null) {
e.initCause(future.cause());
} else {
e.addSuppressed(future.cause());
}
}
});
return e;
}
@Override
public boolean succeeded() {
return consumers.values().stream().allMatch(Future::succeeded);
}
@Override
public boolean failed() {
return consumers.values().stream().anyMatch(Future::failed);
}
@Override
public boolean isComplete() {
if (super.isComplete()) { // has been marked explicitly
return true;
}
return !consumers.isEmpty() &&
consumers.values().stream().allMatch(Future::isComplete);
}
public MultipleFutures<T> join(Future<T> future) {
setHandler(completeOrFail(future));
return this;
}
public MultipleFutures<T> join(Handler<AsyncResult<T>> handler) {
setHandler(handler);
return this;
}
public <L> MultipleFutures<T> addAll(Collection<L> list, BiConsumer<L, Future<T>> transform) {
list.forEach(element -> add(res -> transform.accept(element, res)));
return this;
}
public <L> MultipleFutures<T> addAll(Collection<L> list, Function<L, Handler<Future<T>>> transform) {
list.forEach(element -> add(transform.apply(element)));
return this;
}
public <L> MultipleFutures<T> treatAllNow(Collection<L> list, Function<L, Handler<Future<T>>> transform) {
addAll(list, transform);
start();
return this;
}
public <K, V> MultipleFutures<T> addAll(Map<K, V> map, TriConsumer<K, V, Future<T>> transform) {
map.forEach((key, value) -> add(res -> transform.accept(key, value, res)));
return this;
}
}