/* * Copyright 2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License */ package net.jodah.failsafe; import java.util.concurrent.Callable; import java.util.concurrent.ScheduledExecutorService; import net.jodah.failsafe.Functions.ContextualCallableWrapper; import net.jodah.failsafe.function.CheckedRunnable; import net.jodah.failsafe.function.ContextualCallable; import net.jodah.failsafe.function.ContextualRunnable; import net.jodah.failsafe.internal.util.Assert; import net.jodah.failsafe.util.concurrent.Scheduler; import net.jodah.failsafe.util.concurrent.Schedulers; /** * Performs synchronous executions with failures handled according to a configured {@link #with(RetryPolicy) retry * policy}, {@link #with(CircuitBreaker) circuit breaker} and * {@link #withFallback(net.jodah.failsafe.function.BiFunction) fallback}. * * @author Jonathan Halterman * @param <R> listener result type */ public class SyncFailsafe<R> extends FailsafeConfig<R, SyncFailsafe<R>> { SyncFailsafe(CircuitBreaker circuitBreaker) { this.circuitBreaker = circuitBreaker; } SyncFailsafe(RetryPolicy retryPolicy) { this.retryPolicy = retryPolicy; } /** * Executes the {@code callable} until a successful result is returned or the configured {@link RetryPolicy} is * exceeded. * * @throws NullPointerException if the {@code callable} is null * @throws FailsafeException if the {@code callable} fails with a checked Exception or if interrupted while waiting to * perform a retry. * @throws CircuitBreakerOpenException if a configured circuit is open. */ public <T> T get(Callable<T> callable) { return call(Assert.notNull(callable, "callable")); } /** * Executes the {@code callable} until a successful result is returned or the configured {@link RetryPolicy} is * exceeded. * * @throws NullPointerException if the {@code callable} is null * @throws FailsafeException if the {@code callable} fails with a checked Exception or if interrupted while waiting to * perform a retry. * @throws CircuitBreakerOpenException if a configured circuit is open. */ public <T> T get(ContextualCallable<T> callable) { return call(Functions.callableOf(callable)); } /** * Executes the {@code runnable} until successful or until the configured {@link RetryPolicy} is exceeded. * * @throws NullPointerException if the {@code runnable} is null * @throws FailsafeException if the {@code callable} fails with a checked Exception or if interrupted while waiting to * perform a retry. * @throws CircuitBreakerOpenException if a configured circuit is open. */ public void run(CheckedRunnable runnable) { call(Functions.callableOf(runnable)); } /** * Executes the {@code runnable} until successful or until the configured {@link RetryPolicy} is exceeded. * * @throws NullPointerException if the {@code runnable} is null * @throws FailsafeException if the {@code runnable} fails with a checked Exception or if interrupted while waiting to * perform a retry. * @throws CircuitBreakerOpenException if a configured circuit is open. */ public void run(ContextualRunnable runnable) { call(Functions.callableOf(runnable)); } /** * Creates and returns a new AsyncFailsafe instance that will perform executions and retries asynchronously via the * {@code executor}. * * @throws NullPointerException if {@code executor} is null */ public AsyncFailsafe<R> with(ScheduledExecutorService executor) { return new AsyncFailsafe<R>(this, Schedulers.of(executor)); } /** * Creates and returns a new AsyncFailsafe instance that will perform executions and retries asynchronously via the * {@code scheduler}. * * @throws NullPointerException if {@code scheduler} is null */ public AsyncFailsafe<R> with(Scheduler scheduler) { return new AsyncFailsafe<R>(this, Assert.notNull(scheduler, "scheduler")); } /** * Calls the {@code callable} synchronously, performing retries according to the {@code retryPolicy}. * * @throws FailsafeException if the {@code callable} fails with a checked Exception or if interrupted while waiting to * perform a retry. * @throws CircuitBreakerOpenException if a configured circuit breaker is open */ @SuppressWarnings("unchecked") private <T> T call(Callable<T> callable) { Execution execution = new Execution((FailsafeConfig<Object, ?>) this); // Handle contextual calls if (callable instanceof ContextualCallableWrapper) ((ContextualCallableWrapper<T>) callable).inject(execution); T result = null; Throwable failure; while (true) { if (circuitBreaker != null && !circuitBreaker.allowsExecution()) { CircuitBreakerOpenException e = new CircuitBreakerOpenException(); if (fallback != null) return fallbackFor((R) result, e); throw e; } try { execution.before(); failure = null; result = callable.call(); } catch (Throwable t) { // Re-throw nested execution interruptions if (t instanceof FailsafeException && InterruptedException.class.isInstance(t.getCause())) throw (FailsafeException) t; failure = t; } // Attempt to complete execution if (execution.complete(result, failure, true)) { if (execution.success || (failure == null && fallback == null)) return result; if (fallback != null) return fallbackFor((R) result, failure); throw failure instanceof RuntimeException ? (RuntimeException) failure : new FailsafeException(failure); } else { try { Thread.sleep(execution.getWaitTime().toMillis()); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new FailsafeException(e); } handleRetry((R) result, failure, execution); } } } @SuppressWarnings("unchecked") private <T> T fallbackFor(R result, Throwable failure) { try { return (T) fallback.apply(result, failure); } catch (Exception e) { throw e instanceof RuntimeException ? (RuntimeException) e : new FailsafeException(e); } } }