/** * Same as exceptionallyExpecting(), except that it allows executing/returning a Future as a result in case of an * expected exception. * If such an exception is caught, the given exceptionFutureSupplier is invoked and its result is then returned. * All other Exceptions will be re-thrown. * * @param future The original CompletableFuture to attach to. * @param isExpected A Predicate that can check whether an Exception is expected or not. * @param exceptionFutureSupplier A Supplier that returns a CompletableFuture which will be invoked in case the thrown * Exception if of type exceptionClass. * @param <T> The Type of the Future's result. * @return A new CompletableFuture that will complete either: * - With the same result as the original Future if that one completed normally * - With exceptionValue if the original Future completed with an expected exception. * - Exceptionally with the original Future's exception if none of the above are true. */ public static <T> CompletableFuture<T> exceptionallyComposeExpecting(CompletableFuture<T> future, Predicate<Throwable> isExpected, Supplier<CompletableFuture<T>> exceptionFutureSupplier) { return exceptionallyCompose(future, ex -> { if (isExpected.test(Exceptions.unwrap(ex))) { return exceptionFutureSupplier.get(); } else { return Futures.failedFuture(ex); } }); }
/** * Tests the exceptionallyCompose method. */ @Test public void testExceptionallyCompose() { // When applied to a CompletableFuture that completes normally. val successfulFuture = new CompletableFuture<Integer>(); val f1 = Futures.<Integer>exceptionallyCompose(successfulFuture, ex -> CompletableFuture.completedFuture(2)); successfulFuture.complete(1); Assert.assertEquals("Unexpected completion value for successful future.", 1, (int) f1.join()); // When applied to a CompletableFuture that completes exceptionally. val failedFuture = new CompletableFuture<Integer>(); val f2 = Futures.<Integer>exceptionallyCompose(failedFuture, ex -> CompletableFuture.completedFuture(2)); failedFuture.completeExceptionally(new IntentionalException()); Assert.assertEquals("Unexpected completion value for failed future that handled the exception.", 2, (int) f2.join()); // When applied to a CompletableFuture that completes exceptionally and the handler also throws. val f3 = Futures.<Integer>exceptionallyCompose(failedFuture, ex -> { throw new IntentionalException(); }); AssertExtensions.assertSuppliedFutureThrows( "Unexpected completion for failed future whose handler also threw an exception.", () -> f3, ex -> ex instanceof IntentionalException); }
CompletableFuture<Void> result = getBucketOffsets(segment, hashes, timer) .thenAccept(offsets -> validateConditionalUpdate(batch.getVersionedItems(), offsets, segment.getInfo().getName())); return Futures.exceptionallyCompose( result, ex -> {
/** * Executes the given Index Operation once, without performing retries. In case of failure with BadOffsetException * (which indicates a conditional update failure), the BTreeIndex is reinitialized to the most up-to-date state. * * @param indexOperation A Function, that, when invoked, returns a CompletableFuture which indicates when the index * operation completes. * @param timer Timer for the operation. * @return A CompletableFuture that will indicate when the operation completes. */ private CompletableFuture<Long> executeConditionallyOnce(Function<Duration, CompletableFuture<Long>> indexOperation, TimeoutTimer timer) { return Futures.exceptionallyCompose( indexOperation.apply(timer.getRemaining()), ex -> { if (Exceptions.unwrap(ex) instanceof BadOffsetException) { BadOffsetException boe = (BadOffsetException) Exceptions.unwrap(ex); if (boe.getExpectedOffset() != this.index.getIndexLength()) { log.warn("{}: Conditional Index Update failed (expected {}, given {}). Reinitializing index.", this.traceObjectId, boe.getExpectedOffset(), boe.getGivenOffset()); return this.index.initialize(timer.getRemaining()) .thenCompose(v -> Futures.failedFuture(ex)); } } // Make sure the exception bubbles up. return Futures.failedFuture(ex); }); }
return Futures.exceptionallyCompose( this.durableLog.add(operation, timer.getRemaining()), ex -> {