[case28] Talk about fallback of resilience4j

  resilience4j

Order

This article mainly studies the fallback of resilience4j

Use instance

    @Test
    public void testFallback(){
        // Execute the decorated supplier and recover from any exception
        String result = Try.ofSupplier(() -> backendService.doSomethingThrowException())
                .recover(throwable -> "Hello from Recovery").get();
        System.out.println(result);
    }

Try

vavr-0.9.2-sources.jar! /io/vavr/control/Try.java

/**
 * The Try control gives us the ability write safe code without focusing on try-catch blocks in the presence of exceptions.
 * <p>
 * The following exceptions are considered to be fatal/non-recoverable:
 * <ul>
 * <li>{@linkplain InterruptedException}</li>
 * <li>{@linkplain LinkageError}</li>
 * <li>{@linkplain ThreadDeath}</li>
 * <li>{@linkplain VirtualMachineError} (i.e. {@linkplain OutOfMemoryError} or {@linkplain StackOverflowError})</li>
 * </ul>
 * <p>
 * <strong>Important note:</strong> Try may re-throw (undeclared) exceptions, e.g. on {@code get()}. From within a
 * dynamic proxy {@link java.lang.reflect.InvocationHandler} this will lead to an
 * {@link java.lang.reflect.UndeclaredThrowableException}. For more information, please read
 * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/reflection/proxy.html">Dynamic Proxy Classes</a>.
 *
 * @param <T> Value type in the case of success.
 * @author Daniel Dietrich
 */
public interface Try<T> extends Value<T>, Serializable {

    long serialVersionUID = 1L;

    /**
     * Creates a Try of a CheckedFunction0.
     *
     * @param supplier A checked supplier
     * @param <T>      Component type
     * @return {@code Success(supplier.apply())} if no exception occurs, otherwise {@code Failure(throwable)} if an
     * exception occurs calling {@code supplier.apply()}.
     */
    static <T> Try<T> of(CheckedFunction0<? extends T> supplier) {
        Objects.requireNonNull(supplier, "supplier is null");
        try {
            return new Success<>(supplier.apply());
        } catch (Throwable t) {
            return new Failure<>(t);
        }
    }


    /**
     * Creates a Try of a Supplier.
     *
     * @param supplier A supplier
     * @param <T>      Component type
     * @return {@code Success(supplier.get())} if no exception occurs, otherwise {@code Failure(throwable)} if an
     * exception occurs calling {@code supplier.get()}.
     */
    static <T> Try<T> ofSupplier(Supplier<? extends T> supplier) {
        Objects.requireNonNull(supplier, "supplier is null");
        return of(supplier::get);
    }

    /**
     * Creates a Try of a Callable.
     *
     * @param callable A callable
     * @param <T>      Component type
     * @return {@code Success(callable.call())} if no exception occurs, otherwise {@code Failure(throwable)} if an
     * exception occurs calling {@code callable.call()}.
     */
    static <T> Try<T> ofCallable(Callable<? extends T> callable) {
        Objects.requireNonNull(callable, "callable is null");
        return of(callable::call);
    }

    //......
}
  • This Try inherits the Value interface.
  • In addition, some static factory methods are provided. the ofSupplier method will trigger the execution of the method. if Success is returned successfully, Failure will be returned in case of exception.

Try.Success

vavr-0.9.2-sources.jar! /io/vavr/control/Try.java

   /**
     * A succeeded Try.
     *
     * @param <T> component type of this Success
     * @author Daniel Dietrich
     */
    final class Success<T> implements Try<T>, Serializable {

        private static final long serialVersionUID = 1L;

        private final T value;

        /**
         * Constructs a Success.
         *
         * @param value The value of this Success.
         */
        private Success(T value) {
            this.value = value;
        }

        @Override
        public T get() {
            return value;
        }

        @Override
        public Throwable getCause() {
            throw new UnsupportedOperationException("getCause on Success");
        }

        @Override
        public boolean isEmpty() {
            return false;
        }

        @Override
        public boolean isFailure() {
            return false;
        }

        @Override
        public boolean isSuccess() {
            return true;
        }

        @Override
        public boolean equals(Object obj) {
            return (obj == this) || (obj instanceof Success && Objects.equals(value, ((Success<?>) obj).value));
        }

        @Override
        public int hashCode() {
            return Objects.hashCode(value);
        }

        @Override
        public String stringPrefix() {
            return "Success";
        }

        @Override
        public String toString() {
            return stringPrefix() + "(" + value + ")";
        }
    }
  • IsFailure method returned false

Try.Failure

vavr-0.9.2-sources.jar! /io/vavr/control/Try.java

    /**
     * A failed Try.
     *
     * @param <T> component type of this Failure
     * @author Daniel Dietrich
     */
    final class Failure<T> implements Try<T>, Serializable {

        private static final long serialVersionUID = 1L;

        private final Throwable cause;

        /**
         * Constructs a Failure.
         *
         * @param cause A cause of type Throwable, may not be null.
         * @throws NullPointerException if {@code cause} is null
         * @throws Throwable            if the given {@code cause} is fatal, i.e. non-recoverable
         */
        private Failure(Throwable cause) {
            Objects.requireNonNull(cause, "cause is null");
            if (isFatal(cause)) {
                sneakyThrow(cause);
            }
            this.cause = cause;
        }

        @Override
        public T get() {
            return sneakyThrow(cause);
        }

        @Override
        public Throwable getCause() {
            return cause;
        }

        @Override
        public boolean isEmpty() {
            return true;
        }

        @Override
        public boolean isFailure() {
            return true;
        }

        @Override
        public boolean isSuccess() {
            return false;
        }

        @Override
        public boolean equals(Object obj) {
            return (obj == this) || (obj instanceof Failure && Arrays.deepEquals(cause.getStackTrace(), ((Failure<?>) obj).cause.getStackTrace()));
        }

        @Override
        public String stringPrefix() {
            return "Failure";
        }

        @Override
        public int hashCode() {
            return Arrays.hashCode(cause.getStackTrace());
        }

        @Override
        public String toString() {
            return stringPrefix() + "(" + cause + ")";
        }

    }
  • IsFailure method returns true

recover

vavr-0.9.2-sources.jar! /io/vavr/control/Try.java

    /**
     * Returns {@code this}, if this is a {@code Success}, otherwise tries to recover the exception of the failure with {@code f},
     * i.e. calling {@code Try.of(() -> f.apply(throwable))}.
     *
     * @param f A recovery function taking a Throwable
     * @return a {@code Try}
     * @throws NullPointerException if {@code f} is null
     */
    default Try<T> recover(Function<? super Throwable, ? extends T> f) {
        Objects.requireNonNull(f, "f is null");
        if (isFailure()) {
            return Try.of(() -> f.apply(getCause()));
        } else {
            return this;
        }
    }
  • This recover method first determines whether the failure occurs through the isFailure method.
  • If it fails, the fallback method is called.

Summary

  • Resilience4j’s Try has two implementations, one is Success and the other is Failure, both of which implement the isfailure method.
  • The Try interface has a default method recover, which is used to implement fallback. it first determines whether the method call failed, and if so, executes the fallback method.
  • The whole fallback implementation is similar to try catch and then calls the fallback method.

doc