class FailurePolicyBuilder<S, C extends FailurePolicyConfig<R>, R> extends PolicyBuilder<S, C, R> {
  protected FailurePolicyBuilder(C config) {
    super(config);
  }

  /**
   * Specifies the exception to handle as a failure. Any exception that is assignable from the {@code exception} will be
   * handled.
   *
   * @throws NullPointerException if {@code exception} is null
   */
  public S handle(Class<? extends Throwable> exception) {
    Assert.notNull(exception, "exception");
    return handle(Arrays.asList(exception));
  }

  /**
   * Specifies the exceptions to handle as failures. Any exceptions that are assignable from the {@code exceptions} will
   * be handled.
   *
   * @throws NullPointerException if {@code exceptions} is null
   * @throws IllegalArgumentException if exceptions is empty
   */
  @SafeVarargs
  public final S handle(Class<? extends Throwable>... exceptions) {
    Assert.notNull(exceptions, "exceptions");
    Assert.isTrue(exceptions.length > 0, "exceptions cannot be empty");
    return handle(Arrays.asList(exceptions));
  }

  /**
   * Specifies the exceptions to handle as failures. Any exceptions that are assignable from the {@code exceptions} will
   * be handled.
   *
   * @throws NullPointerException if {@code exceptions} is null
   * @throws IllegalArgumentException if exceptions is null or empty
   */
  public S handle(List<Class<? extends Throwable>> exceptions) {
    Assert.notNull(exceptions, "exceptions");
    Assert.isTrue(!exceptions.isEmpty(), "exceptions cannot be empty");
    config.exceptionsChecked = true;
    config.failureConditions.add(failurePredicateFor(exceptions));
    return (S) this;
  }

  /**
   * Specifies that a failure has occurred if the {@code failurePredicate} matches the exception. Any exception thrown
   * from the {@code failurePredicate} is treated as a {@code false} result.
   *
   * @throws NullPointerException if {@code failurePredicate} is null
   */
  public S handleIf(CheckedPredicate<? extends Throwable> failurePredicate) {
    Assert.notNull(failurePredicate, "failurePredicate");
    config.exceptionsChecked = true;
    config.failureConditions.add(failurePredicateFor(failurePredicate));
    return (S) this;
  }

  /**
   * Specifies that a failure has occurred if the {@code resultPredicate} matches the execution result. Any exception
   * thrown from the {@code resultPredicate} is treated as a {@code false} result.
   *
   * @throws NullPointerException if {@code resultPredicate} is null
   */
  @SuppressWarnings("unchecked")
  public S handleIf(CheckedBiPredicate<R, ? extends Throwable> resultPredicate) {<FILL_FUNCTION_BODY>}

  /**
   * Specifies that a failure has occurred if the {@code result} matches the execution result. This method is only
   * considered when a result is returned from an execution, not when an exception is thrown.
   */
  public S handleResult(R result) {
    config.failureConditions.add(resultPredicateFor(result));
    return (S) this;
  }

  /**
   * Specifies that a failure has occurred if the {@code resultPredicate} matches the execution result. This method is
   * only considered when a result is returned from an execution, not when an exception is thrown. To handle results or
   * exceptions with the same condition, use {@link #handleIf(CheckedBiPredicate)}. Any exception thrown from the {@code
   * resultPredicate} is treated as a {@code false} result.
   *
   * @throws NullPointerException if {@code resultPredicate} is null
   */
  public S handleResultIf(CheckedPredicate<R> resultPredicate) {
    Assert.notNull(resultPredicate, "resultPredicate");
    config.failureConditions.add(resultPredicateFor(resultPredicate));
    return (S) this;
  }

  /**
   * Returns a predicate that evaluates whether the {@code result} equals an execution result.
   */
  static <R> CheckedBiPredicate<R, Throwable> resultPredicateFor(R result) {
    return (t, u) -> result == null ? t == null && u == null : Objects.equals(result, t);
  }

  /**
   * Returns a predicate that evaluates the {@code failurePredicate} against a failure.
   */
  @SuppressWarnings("unchecked")
  static <R> CheckedBiPredicate<R, Throwable> failurePredicateFor(
    CheckedPredicate<? extends Throwable> failurePredicate) {
    return (t, u) -> u != null && ((CheckedPredicate<Throwable>) failurePredicate).test(u);
  }

  /**
   * Returns a predicate that evaluates the {@code resultPredicate} against a result, when present.
   * <p>
   * Short-circuits to false without invoking {@code resultPredicate}, when result is not present (i.e.
   * BiPredicate.test(null, Throwable)).
   */
  static <R> CheckedBiPredicate<R, Throwable> resultPredicateFor(CheckedPredicate<R> resultPredicate) {
    return (t, u) -> {
      if (u == null) {
        return resultPredicate.test(t);
      } else {
        // resultPredicate is only defined over the success type.
        // It doesn't know how to handle a failure of type Throwable,
        // so we return false here.
        return false;
      }
    };
  }

  /**
   * Returns a predicate that returns whether any of the {@code failures} are assignable from an execution failure.
   */
  static <R> CheckedBiPredicate<R, Throwable> failurePredicateFor(List<Class<? extends Throwable>> failures) {
    return (t, u) -> {
      if (u == null)
        return false;
      for (Class<? extends Throwable> failureType : failures)
        if (failureType.isAssignableFrom(u.getClass()))
          return true;
      return false;
    };
  }
}

    Assert.notNull(resultPredicate, "resultPredicate");
    config.exceptionsChecked = true;
    config.failureConditions.add((CheckedBiPredicate<R, Throwable>) resultPredicate);
    return (S) this;
  
<methods>public S onFailure(EventListener<ExecutionCompletedEvent<R>>) ,public S onSuccess(EventListener<ExecutionCompletedEvent<R>>) <variables>protected C config