static AsyncRetrySupervisor of(final List<RetryRule> retryRules) { return new AsyncRetrySupervisorImpl(retryRules); } }
private <P, R> void retry(final RetryContextImpl<P, R> retryContext, final Function<P, CompletionStage<R>> function, final Object parameter) { final CompletionStage<R> completionStage = forceApply(function, parameter); handleResultAndEnqueueErrorHandlingAgain(completionStage, parameter, retryContext); }
private <P, R> void handle(final RetryContextImpl<P, R> retryContext) { final RetryStrategy retryStrategy = applyContext(retryContext); final StrategyType strategyType = retryStrategy.getStrategyType(); if (strategyType == StrategyType.RESUME || strategyType == StrategyType.STOP) { retryContext.getResult().completeExceptionally(retryStrategy.getError()); if (strategyType == StrategyType.STOP) { closeService(retryContext); } } else { final Function<P, CompletionStage<R>> function = retryContext.getFunction(); final Object parameter = retryStrategy.getParameter(); if (strategyType == StrategyType.RETRY_IMMEDIATELY) { retry(retryContext, function, parameter); } else if (strategyType == StrategyType.RETRY_SCHEDULED) { final Duration duration = retryStrategy.getDuration(); retryContext.schedule(() -> retry(retryContext, function, parameter), duration); } else { throw new IllegalStateException("illegal state for " + retryStrategy); } } }
@Override public <P, R> CompletionStage<R> supervise(final AutoCloseable service, final Function<P, CompletionStage<R>> f, @Nullable final P parameterObject) { final CompletableFuture<R> result = new CompletableFuture<>(); try { final CompletionStage<R> initialCompletionStage = f.apply(parameterObject); initialCompletionStage.whenCompleteAsync((res, firstError) -> { final boolean isErrorCase = firstError != null; if (isErrorCase) { final RetryContextImpl<P, R> retryOperationContext = createFirstRetryOperationContext(firstError, result, f, parameterObject, service); handle(retryOperationContext); } else { result.complete(res); } }, executor); } catch (final Throwable e) {//necessary if f.apply() throws directly an exception result.completeExceptionally(e); } return result; }
private <P, R> void handleResultAndEnqueueErrorHandlingAgain(final CompletionStage<R> completionStage, final Object parameter, final RetryContextImpl<P, R> retryOperationContext) { completionStage.whenCompleteAsync((res, error) -> { final boolean isErrorCase = error != null; if (isErrorCase) { final RetryContextImpl<P, R> nextContext = retryOperationContext.withNewFailedAttempt(error, parameter); handle(nextContext); } else { retryOperationContext.getResult().complete(res); } }, executor); }
private RetryStrategy applyContext(final RetryContext retryContext) { final Optional<RetryRule> matchingRetryRuleOption = findMatchingRetryRule(retryRules, retryContext); final RetryRule matchingRetryRule = matchingRetryRuleOption .orElseGet(() -> RetryRule.of(RetryPredicate.ofAlwaysTrue(), RetryAction.ofGiveUpAndSendLatestException())); return matchingRetryRule.apply(retryContext); }