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}