From b16af62dd6b6e81310134721c72de5061019a68e Mon Sep 17 00:00:00 2001 From: Julian Ladisch Date: Wed, 4 Nov 2020 19:12:50 +0100 Subject: [PATCH] OKAPI-943: GenericCompositeFuture Coordinate a List>>, provides all(List), any(List) and join(List) that work like CompositeFuture#all(List), CompositeFuture#any(List) and CompositeFuture#join(List) but properly check the generic type >. See https://github.com/eclipse-vertx/vert.x/pull/3595 why Vert.x doesn't have this. --- okapi-common/pom.xml | 5 + .../okapi/common/GenericCompositeFuture.java | 59 ++++++++++ .../common/GenericCompositeFutureTest.java | 103 ++++++++++++++++++ 3 files changed, 167 insertions(+) create mode 100644 okapi-common/src/main/java/org/folio/okapi/common/GenericCompositeFuture.java create mode 100644 okapi-common/src/test/java/org/folio/okapi/common/GenericCompositeFutureTest.java diff --git a/okapi-common/pom.xml b/okapi-common/pom.xml index e6e8934ac..98694100b 100644 --- a/okapi-common/pom.xml +++ b/okapi-common/pom.xml @@ -71,6 +71,11 @@ vertx-unit test + + org.assertj + assertj-core + test + diff --git a/okapi-common/src/main/java/org/folio/okapi/common/GenericCompositeFuture.java b/okapi-common/src/main/java/org/folio/okapi/common/GenericCompositeFuture.java new file mode 100644 index 000000000..9ce00594b --- /dev/null +++ b/okapi-common/src/main/java/org/folio/okapi/common/GenericCompositeFuture.java @@ -0,0 +1,59 @@ +package org.folio.okapi.common; + +import io.vertx.core.CompositeFuture; +import io.vertx.core.Future; +import io.vertx.core.impl.future.CompositeFutureImpl; +import java.util.List; + +/** + * Coordinate a {@code List>>}, provides + * {@link #all(List) all(List<T>)}, {@link #any(List) any(List<T>)} and + * {@link #join(List) join(List<T>)} that work like + * {@link CompositeFuture#all(List)}, {@link CompositeFuture#any(List)} and + * {@link CompositeFuture#join(List)} but properly check the generic type + * {@code >}. + *

+ * See + * https://github.com/eclipse-vertx/vert.x/pull/3595 + * why Vert.x doesn't have this. + */ +public interface GenericCompositeFuture { + /** + * Return a composite future, succeeded when all futures are succeeded, + * failed as soon as one of the futures is failed. + *

+ * When the list is empty, the returned future will be already succeeded. + *

+ * This is the same as {@link CompositeFuture#all(List)} but with generic type declaration. + */ + static > CompositeFuture all(List futures) { + return CompositeFutureImpl.all(futures.toArray(new Future[futures.size()])); + } + + /** + * Return a composite future, succeeded as soon as one future is succeeded, + * failed when all futures are failed. + *

+ * When the list is empty, the returned future will be already succeeded. + *

+ * This is the same as {@link CompositeFuture#any(List)} but with generic type declaration. + */ + static > CompositeFuture any(List futures) { + return CompositeFutureImpl.any(futures.toArray(new Future[futures.size()])); + } + + /** + * Return a composite future, succeeded when all {@code futures} are succeeded, + * failed when any of the {@code futures} is failed. + *

+ * It always waits until all its {@code futures} are completed and will not fail + * as soon as one of the {@code futures} fails. + *

+ * When the list is empty, the returned future will be already succeeded. + *

+ * This is the same as {@link CompositeFuture#join(List)} but with generic type declaration. + */ + static > CompositeFuture join(List futures) { + return CompositeFutureImpl.join(futures.toArray(new Future[futures.size()])); + } +} diff --git a/okapi-common/src/test/java/org/folio/okapi/common/GenericCompositeFutureTest.java b/okapi-common/src/test/java/org/folio/okapi/common/GenericCompositeFutureTest.java new file mode 100644 index 000000000..39b75a421 --- /dev/null +++ b/okapi-common/src/test/java/org/folio/okapi/common/GenericCompositeFutureTest.java @@ -0,0 +1,103 @@ +package org.folio.okapi.common; + +import io.vertx.core.AsyncResult; +import io.vertx.core.CompositeFuture; +import io.vertx.core.Future; +import io.vertx.core.Handler; +import io.vertx.core.Promise; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.function.Function; +import org.assertj.core.api.WithAssertions; +import org.junit.Test; + +public class GenericCompositeFutureTest implements WithAssertions { + @Test + public void success() { + List> successSuccess = Arrays.asList(Future.succeededFuture(), Future.succeededFuture()); + List> successFailure = Arrays.asList(Future.succeededFuture(), Future.failedFuture("")); + assertThat(GenericCompositeFuture.all(successSuccess).succeeded()).isTrue(); + assertThat(GenericCompositeFuture.all(successFailure).failed()).isTrue(); + assertThat(GenericCompositeFuture.any(successSuccess).succeeded()).isTrue(); + assertThat(GenericCompositeFuture.any(successFailure).succeeded()).isTrue(); + assertThat(GenericCompositeFuture.join(successSuccess).succeeded()).isTrue(); + assertThat(GenericCompositeFuture.join(successFailure).failed()).isTrue(); + } + + @Test + public void completion() { + List> failureAndUnknown = Arrays.asList(Future.failedFuture(""), Promise.promise().future()); + assertThat(GenericCompositeFuture.all(failureAndUnknown).isComplete()).isTrue(); + assertThat(GenericCompositeFuture.any(failureAndUnknown).isComplete()).isFalse(); + assertThat(GenericCompositeFuture.join(failureAndUnknown).isComplete()).isFalse(); + } + + @Test + public void generics() { + List> list = Collections.emptyList(); + CompositeFuture all = GenericCompositeFuture.all(list); + CompositeFuture any = GenericCompositeFuture.any(list); + CompositeFuture join = GenericCompositeFuture.join(list); + assertThat(all.succeeded()).isTrue(); + assertThat(any.succeeded()).isTrue(); + assertThat(join.succeeded()).isTrue(); + } + + class MyFuture implements Future { + @Override + public boolean isComplete() { + return false; + } + + @Override + public Future onComplete(Handler> handler) { + return null; + } + + @Override + public T result() { + return null; + } + + @Override + public Throwable cause() { + return null; + } + + @Override + public boolean succeeded() { + return false; + } + + @Override + public boolean failed() { + return false; + } + + @Override + public Future compose(Function> successMapper, Function> failureMapper) { + return null; + } + + @Override + public Future map(Function mapper) { + return null; + } + + @Override + public Future map(V value) { + return null; + } + + @Override + public Future otherwise(Function mapper) { + return null; + } + + @Override + public Future otherwise(T value) { + return null; + } + }; +}