/* * 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.Future; import java.util.concurrent.TimeUnit; import net.jodah.failsafe.Functions.AsyncCallableWrapper; import net.jodah.failsafe.function.AsyncCallable; import net.jodah.failsafe.function.AsyncRunnable; import net.jodah.failsafe.function.CheckedBiFunction; import net.jodah.failsafe.function.CheckedRunnable; import net.jodah.failsafe.function.ContextualCallable; import net.jodah.failsafe.function.ContextualRunnable; import net.jodah.failsafe.util.concurrent.Scheduler; /** * Performs asynchronous 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.CheckedBiFunction) fallback}. * * @author Jonathan Halterman * @param <R> listener result type */ public class AsyncFailsafe<R> extends AsyncFailsafeConfig<R, AsyncFailsafe<R>> { AsyncFailsafe(FailsafeConfig<R, ?> config, Scheduler scheduler) { super(config, scheduler); } /** * Executes the {@code callable} asynchronously until the resulting future is successfully completed or the configured * {@link RetryPolicy} is exceeded. * <p> * If a configured circuit breaker is open, the resulting future is completed exceptionally with * {@link CircuitBreakerOpenException}. * <p> * Supported on Java 8 and above. * * @throws NullPointerException if the {@code callable} is null */ public <T> java.util.concurrent.CompletableFuture<T> future( Callable<java.util.concurrent.CompletableFuture<T>> callable) { return call(Functions.asyncOfFuture(callable)); } /** * Executes the {@code callable} asynchronously until the resulting future is successfully completed or the configured * {@link RetryPolicy} is exceeded. * <p> * If a configured circuit breaker is open, the resulting future is completed exceptionally with * {@link CircuitBreakerOpenException}. * <p> * Supported on Java 8 and above. * * @throws NullPointerException if the {@code callable} is null */ public <T> java.util.concurrent.CompletableFuture<T> future( ContextualCallable<java.util.concurrent.CompletableFuture<T>> callable) { return call(Functions.asyncOfFuture(callable)); } /** * Executes the {@code callable} asynchronously until the resulting future is successfully completed or the configured * {@link RetryPolicy} is exceeded. This method is intended for integration with asynchronous code. Retries must be * manually scheduled via one of the {@code AsyncExecution.retry} methods. * <p> * If a configured circuit breaker is open, the resulting future is completed exceptionally with * {@link CircuitBreakerOpenException}. * <p> * Supported on Java 8 and above. * * @throws NullPointerException if the {@code callable} is null */ public <T> java.util.concurrent.CompletableFuture<T> futureAsync( AsyncCallable<java.util.concurrent.CompletableFuture<T>> callable) { return call(Functions.asyncOfFuture(callable)); } /** * Executes the {@code callable} asynchronously until a successful result is returned or the configured * {@link RetryPolicy} is exceeded. * <p> * If a configured circuit breaker is open, the resulting future is completed with * {@link CircuitBreakerOpenException}. * * @throws NullPointerException if the {@code callable} is null */ public <T> FailsafeFuture<T> get(Callable<T> callable) { return call(Functions.asyncOf(callable), null); } /** * Executes the {@code callable} asynchronously until a successful result is returned or the configured * {@link RetryPolicy} is exceeded. * <p> * If a configured circuit breaker is open, the resulting future is completed with * {@link CircuitBreakerOpenException}. * * @throws NullPointerException if the {@code callable} is null */ public <T> FailsafeFuture<T> get(ContextualCallable<T> callable) { return call(Functions.asyncOf(callable), null); } /** * Executes the {@code callable} asynchronously until a successful result is returned or the configured * {@link RetryPolicy} is exceeded. This method is intended for integration with asynchronous code. Retries must be * manually scheduled via one of the {@code AsyncExecution.retry} methods. * <p> * If a configured circuit breaker is open, the resulting future is completed with * {@link CircuitBreakerOpenException}. * * @throws NullPointerException if the {@code callable} is null */ public <T> FailsafeFuture<T> getAsync(AsyncCallable<T> callable) { return call(Functions.asyncOf(callable), null); } /** * Executes the {@code runnable} asynchronously until successful or until the configured {@link RetryPolicy} is * exceeded. * <p> * If a configured circuit breaker is open, the resulting future is completed with * {@link CircuitBreakerOpenException}. * * @throws NullPointerException if the {@code runnable} is null */ public FailsafeFuture<Void> run(CheckedRunnable runnable) { return call(Functions.<Void> asyncOf(runnable), null); } /** * Executes the {@code runnable} asynchronously until successful or until the configured {@link RetryPolicy} is * exceeded. * <p> * If a configured circuit breaker is open, the resulting future is completed with * {@link CircuitBreakerOpenException}. * * @throws NullPointerException if the {@code runnable} is null */ public FailsafeFuture<Void> run(ContextualRunnable runnable) { return call(Functions.<Void> asyncOf(runnable), null); } /** * Executes the {@code runnable} asynchronously until successful or until the configured {@link RetryPolicy} is * exceeded. This method is intended for integration with asynchronous code. Retries must be manually scheduled via * one of the {@code AsyncExecution.retry} methods. * <p> * If a configured circuit breaker is open, the resulting future is completed with * {@link CircuitBreakerOpenException}. * * @throws NullPointerException if the {@code runnable} is null */ public FailsafeFuture<Void> runAsync(AsyncRunnable runnable) { return call(Functions.<Void> asyncOf(runnable), null); } /** * Calls the asynchronous {@code callable} via the configured Scheduler, performing retries according to the * configured RetryPolicy, and returns a CompletableFuture. * <p> * If a configured circuit breaker is open, the resulting future is completed with * {@link CircuitBreakerOpenException}. * * @throws NullPointerException if any argument is null */ @SuppressWarnings("unchecked") private <T> java.util.concurrent.CompletableFuture<T> call(AsyncCallableWrapper<T> callable) { FailsafeFuture<T> future = new FailsafeFuture<T>((FailsafeConfig<T, ?>) this); java.util.concurrent.CompletableFuture<T> response = net.jodah.failsafe.internal.util.CancellableFuture.of(future); future.inject(response); call(callable, future); return response; } /** * Calls the asynchronous {@code callable} via the configured Scheduler, performing retries according to the * configured RetryPolicy. * <p> * If a configured circuit breaker is open, the resulting future is completed with * {@link CircuitBreakerOpenException}. * * @throws NullPointerException if any argument is null */ @SuppressWarnings("unchecked") private <T> FailsafeFuture<T> call(AsyncCallableWrapper<T> callable, FailsafeFuture<T> future) { if (future == null) future = new FailsafeFuture<T>((FailsafeConfig<T, ?>) this); if (circuitBreaker != null && !circuitBreaker.allowsExecution()) { CircuitBreakerOpenException e = new CircuitBreakerOpenException(); future.complete(null, e, (CheckedBiFunction<T, Throwable, T>) fallback, false); return future; } AsyncExecution execution = new AsyncExecution(callable, scheduler, future, (FailsafeConfig<Object, ?>) this); callable.inject(execution); future.inject(execution); try { future.inject((Future<T>) scheduler.schedule(callable, 0, TimeUnit.MILLISECONDS)); } catch (Throwable t) { handleComplete(null, t, execution, false); future.complete(null, t, (CheckedBiFunction<T, Throwable, T>) fallback, false); } return future; } }