Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
net.jodah.failsafe.AsyncExecution Maven / Gradle / Ivy
/*
* 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.internal.util.Assert;
import net.jodah.failsafe.util.concurrent.Scheduler;
/**
* Tracks asynchronous executions and allows retries to be scheduled according to a {@link RetryPolicy}.
*
* @author Jonathan Halterman
*/
public final class AsyncExecution extends AbstractExecution {
private final Callable callable;
private final FailsafeFuture future;
private final Scheduler scheduler;
volatile boolean completeCalled;
volatile boolean retryCalled;
@SuppressWarnings("unchecked")
AsyncExecution(Callable callable, Scheduler scheduler, FailsafeFuture future,
FailsafeConfig config) {
super(config);
this.callable = (Callable) callable;
this.scheduler = scheduler;
this.future = (FailsafeFuture) future;
}
/**
* Completes the execution and the associated {@code FutureResult}.
*
* @throws IllegalStateException if the execution is already complete
*/
public void complete() {
complete(null, null, false);
}
/**
* Attempts to complete the execution and the associated {@code FutureResult} with the {@code result}. Returns true on
* success, else false if completion failed and the execution should be retried via {@link #retry()}.
*
* @throws IllegalStateException if the execution is already complete
*/
public boolean complete(Object result) {
return complete(result, null, true);
}
/**
* Attempts to complete the execution and the associated {@code FutureResult} with the {@code result} and
* {@code failure}. Returns true on success, else false if completion failed and the execution should be retried via
* {@link #retry()}.
*
* Note: the execution may be completed even when the {@code failure} is not {@code null}, such as when the
* RetryPolicy does not allow retries for the {@code failure}.
*
* @throws IllegalStateException if the execution is already complete
*/
public boolean complete(Object result, Throwable failure) {
return complete(result, failure, true);
}
/**
* Records an execution and returns true if a retry has been scheduled for else returns returns false and completes
* the execution and associated {@code FutureResult}.
*
* @throws IllegalStateException if a retry method has already been called or the execution is already complete
*/
public boolean retry() {
Assert.state(!retryCalled, "Retry has already been called");
retryCalled = true;
return completeOrRetry(lastResult, lastFailure);
}
/**
* Records an execution and returns true if a retry has been scheduled for the {@code result}, else returns false and
* marks the execution and associated {@code FutureResult} as complete.
*
* @throws IllegalStateException if a retry method has already been called or the execution is already complete
*/
public boolean retryFor(Object result) {
return retryFor(result, null);
}
/**
* Records an execution and returns true if a retry has been scheduled for the {@code result} or {@code failure}, else
* returns false and marks the execution and associated {@code FutureResult} as complete.
*
* @throws IllegalStateException if a retry method has already been called or the execution is already complete
*/
public boolean retryFor(Object result, Throwable failure) {
Assert.state(!retryCalled, "Retry has already been called");
retryCalled = true;
return completeOrRetry(result, failure);
}
/**
* Records an execution and returns true if a retry has been scheduled for the {@code failure}, else returns false and
* marks the execution and associated {@code FutureResult} as complete.
*
* @throws NullPointerException if {@code failure} is null
* @throws IllegalStateException if a retry method has already been called or the execution is already complete
*/
public boolean retryOn(Throwable failure) {
Assert.notNull(failure, "failure");
return retryFor(null, failure);
}
/**
* Prepares for an execution retry by recording the start time, checking if the circuit is open, resetting internal
* flags, and calling the retry listeners.
*/
void before() {
if (config.circuitBreaker != null && !config.circuitBreaker.allowsExecution()) {
completed = true;
Exception failure = new CircuitBreakerOpenException();
if (config != null)
config.handleComplete(null, failure, this, false);
future.complete(null, failure, config.fallback, false);
return;
}
if (completeCalled && config != null)
config.handleRetry(lastResult, lastFailure, this);
super.before();
completeCalled = false;
retryCalled = false;
}
/**
* Attempts to complete the parent execution, calls failure handlers, and completes the future if needed.
*
* @throws IllegalStateException if the execution is already complete
*/
@Override
boolean complete(Object result, Throwable failure, boolean checkArgs) {
synchronized (future) {
if (!completeCalled) {
if (super.complete(result, failure, checkArgs))
future.complete(result, failure, config.fallback, success);
completeCalled = true;
}
return completed;
}
}
/**
* Attempts to complete the execution else schedule a retry, returning whether a retry has been scheduled or not.
*
* @throws IllegalStateException if the execution is already complete
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
boolean completeOrRetry(Object result, Throwable failure) {
synchronized (future) {
if (!complete(result, failure, true) && !future.isDone() && !future.isCancelled()) {
try {
future.inject((Future) scheduler.schedule(callable, delayNanos, TimeUnit.NANOSECONDS));
return true;
} catch (Throwable t) {
failure = t;
if (config != null)
config.handleComplete(null, t, this, false);
future.complete(null, failure, config.fallback, false);
}
}
return false;
}
}
}