net.sf.jabb.util.attempt.AttemptStrategyWithRetryOnResult Maven / Gradle / Ivy
/**
*
*/
package net.sf.jabb.util.attempt;
import java.time.Duration;
import java.util.concurrent.Callable;
import java.util.function.Predicate;
import javax.annotation.Nonnull;
import net.sf.jabb.util.parallel.BackoffStrategy;
import net.sf.jabb.util.parallel.WaitStrategy;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.TimeLimiter;
/**
* The strategy controlling how a Callable will be attempted multiple times.
* @author James Hu
*
* @param Return value type of the Callable
*/
public class AttemptStrategyWithRetryOnResult extends AttemptStrategyImpl {
protected Predicate> retryConditionOnResult;
/**
* Attempt the Callable according to the strategies defined, throwing all the exceptions in their original forms.
* @param callable the Callable to be attempted
* @return the result returned by the Callable
* @throws AttemptException Any exception happened while applying the attempt strategy.
* For example, {@link TooManyAttemptsException} if no more attempt is allowed by the stop strategy,
* or {@link InterruptedBeforeAttemptException} if InterruptedException happened while applying attempt time limit strategy or backoff strategy
* @throws Exception It can be any exception thrown from within the Callable that is considered as non-recoverable by the retry strategies
*/
public R callThrowingAll(Callable callable)
throws AttemptException, Exception {
return callThrowingAll(callable, retryConditionOnResult);
}
/**
* Synonym of {@link #callThrowingAll(Callable)}
* @param callable the Callable to be attempted
* @return the result returned by the Callable
* @throws AttemptException Any exception happened while applying the attempt strategy.
* For example, {@link TooManyAttemptsException} if no more attempt is allowed by the stop strategy,
* or {@link InterruptedBeforeAttemptException} if InterruptedException happened while applying attempt time limit strategy or backoff strategy
* @throws Exception It can be any exception thrown from within the Callable that is considered as non-recoverable by the retry strategies
*/
public R call(Callable callable) throws AttemptException, Exception {
return callThrowingAll(callable, retryConditionOnResult);
}
/**
* Constructor initiating an strategy by copying configurations from another instance.
* @param that another attempt strategy
*/
@SuppressWarnings("unchecked")
AttemptStrategyWithRetryOnResult(AttemptStrategyImpl that){
super(that);
if (that instanceof AttemptStrategyWithRetryOnResult){
this.retryConditionOnResult = ((AttemptStrategyWithRetryOnResult)that).retryConditionOnResult;
}
}
/////////////////////////////////
/**
* Sets the stop strategy used to decide when to stop attempting. The default strategy is to not stop at all .
*
* @param stopStrategy the strategy used to decide when to stop retrying
* @return this
* @throws IllegalStateException if a stop strategy has already been set.
*/
@SuppressWarnings("unchecked")
public AttemptStrategyWithRetryOnResult withStopStrategy(@Nonnull StopStrategy stopStrategy) throws IllegalStateException {
return (AttemptStrategyWithRetryOnResult) super.withStopStrategy(stopStrategy);
}
/**
* Sets the stop strategy used to decide when to stop attempting. The default strategy is to not stop at all .
*
* @param stopStrategy the strategy used to decide when to stop retrying
* @return this
*/
@SuppressWarnings("unchecked")
public AttemptStrategyWithRetryOnResult overrideStopStrategy(@Nonnull StopStrategy stopStrategy){
return (AttemptStrategyWithRetryOnResult) super.overrideStopStrategy(stopStrategy);
}
/**
* Sets the backoff strategy used to decide how long to backoff before next attempt. The default strategy is to not backoff at all .
*
* @param backoffStrategy the strategy used to decide how long to backoff before next attempt
* @return this
* @throws IllegalStateException if a backoff strategy has already been set.
*/
@SuppressWarnings("unchecked")
public AttemptStrategyWithRetryOnResult withBackoffStrategy(@Nonnull AttemptBackoffStrategy backoffStrategy) throws IllegalStateException {
return (AttemptStrategyWithRetryOnResult) super.withBackoffStrategy(backoffStrategy);
}
/**
* Sets the backoff strategy used to decide how long to backoff before next attempt. The default strategy is to not backoff at all .
*
* @param backoffStrategy the strategy used to decide how long to backoff before next attempt
* @return this
*/
@SuppressWarnings("unchecked")
public AttemptStrategyWithRetryOnResult overrideBackoffStrategy(@Nonnull AttemptBackoffStrategy backoffStrategy){
return (AttemptStrategyWithRetryOnResult) super.overrideBackoffStrategy(backoffStrategy);
}
/**
* Sets the backoff strategy used to decide how long to backoff before next attempt. The default strategy is to not backoff at all .
* @param backoffStrategy the strategy used to decide how long to backoff before next attempt
* @return this
* @throws IllegalStateException if a backoff strategy has already been set.
*/
@SuppressWarnings("unchecked")
public AttemptStrategyWithRetryOnResult withBackoffStrategy(@Nonnull BackoffStrategy backoffStrategy) throws IllegalStateException {
return (AttemptStrategyWithRetryOnResult) super.withBackoffStrategy(backoffStrategy);
}
/**
* Sets the backoff strategy used to decide how long to backoff before next attempt. The default strategy is to not backoff at all .
* @param backoffStrategy the strategy used to decide how long to backoff before next attempt
* @return this
*/
@SuppressWarnings("unchecked")
public AttemptStrategyWithRetryOnResult overrideBackoffStrategy(@Nonnull BackoffStrategy backoffStrategy){
return (AttemptStrategyWithRetryOnResult) super.overrideBackoffStrategy(backoffStrategy);
}
/**
* Sets the wait strategy used to decide how to perform waiting before next attempt. The default strategy is to use Thread.sleep() .
*
* @param waitStrategy the strategy used to decide how to perform waiting before next attempt
* @return this
* @throws IllegalStateException if a wait strategy has already been set.
*/
@SuppressWarnings("unchecked")
public AttemptStrategyWithRetryOnResult withWaitStrategy(@Nonnull WaitStrategy waitStrategy) throws IllegalStateException {
return (AttemptStrategyWithRetryOnResult) super.withWaitStrategy(waitStrategy);
}
/**
* Sets the wait strategy used to decide how to perform waiting before next attempt. The default strategy is to use Thread.sleep() .
*
* @param waitStrategy the strategy used to decide how to perform waiting before next attempt
* @return this
*/
@SuppressWarnings("unchecked")
public AttemptStrategyWithRetryOnResult overrideWaitStrategy(@Nonnull WaitStrategy waitStrategy) {
return (AttemptStrategyWithRetryOnResult) super.overrideWaitStrategy(waitStrategy);
}
/**
* Sets the time limit for each of the attempts. By default there will be no time limit applied.
*
* @param attemptTimeLimiter the TimeLimiter implementation
* @param attemptTimeLimit the maximum time allowed for an attempt
* @return this
* @throws IllegalStateException if a time limit has already been set.
*/
@SuppressWarnings("unchecked")
public AttemptStrategyWithRetryOnResult withAttemptTimeLimit(TimeLimiter attemptTimeLimiter, Duration attemptTimeLimit) throws IllegalStateException{
return (AttemptStrategyWithRetryOnResult) super.withAttemptTimeLimit(attemptTimeLimiter, attemptTimeLimit);
}
/**
* Sets the time limit for each of the attempts. By default there will be no time limit applied.
*
* @param attemptTimeLimiter the TimeLimiter implementation
* @param attemptTimeLimit the maximum time allowed for an attempt
* @return this
*/
@SuppressWarnings("unchecked")
public AttemptStrategyWithRetryOnResult overrideAttemptTimeLimit(TimeLimiter attemptTimeLimiter, Duration attemptTimeLimit){
return (AttemptStrategyWithRetryOnResult) super.overrideAttemptTimeLimit(attemptTimeLimiter, attemptTimeLimit);
}
/**
* Add a listener. More than one listeners can be added.
* @param listener a listener
*/
@SuppressWarnings("unchecked")
public AttemptStrategyWithRetryOnResult withAttemptListener(@Nonnull AttemptListener listener) {
return (AttemptStrategyWithRetryOnResult) super.withAttemptListener(listener);
}
/////////////////////////////////
/**
* Adds a predicate to decide whether next attempt is needed when exception happened in previous attempt.
* retryIf*Exceptin(...) methods can be called multiple times, all the predicates will be or-ed.
* If no retryIf*Exceptin(...) has been called or if an exception happened during an attempt cannot
* make any of the predicates true, it will be propageted and there will be no further attempt.
* @param exceptionPredicate The predicate to be called when exception happened in previous attempt.
* It should return true if another attempt is needed.
* @return this
*/
@SuppressWarnings("unchecked")
public AttemptStrategyWithRetryOnResult retryIfAttemptHasException(@Nonnull Predicate> exceptionPredicate) {
return (AttemptStrategyWithRetryOnResult) super.retryIfAttemptHasException(exceptionPredicate);
}
/**
* Adds a predicate to decide whether next attempt is needed when exception happened in previous attempt.
* retryIf*Exceptin(...) methods can be called multiple times, all the predicates will be or-ed.
* If no retryIf*Exceptin(...) has been called or if an exception happened during an attempt cannot
* make any of the predicates true, it will be propageted and there will be no further attempt.
* @param exceptionPredicate The predicate to be called when exception happened in previous attempt.
* It should return true if another attempt is needed.
* @return this
*/
@SuppressWarnings("unchecked")
public AttemptStrategyWithRetryOnResult retryIfException(@Nonnull Predicate exceptionPredicate) {
return (AttemptStrategyWithRetryOnResult) super.retryIfException(exceptionPredicate);
}
/**
* Adds a predicate to decide whether next attempt is needed when exception of specified type happened in previous attempt.
* retryIf*Exceptin(...) methods can be called multiple times, all the predicates will be or-ed.
* If no retryIf*Exceptin(...) has been called or if an exception happened during an attempt cannot
* make any of the predicates true, it will be propagated and there will be no further attempt.
* @param exceptionClass type of the exception that the predicate applies to
* @param exceptionPredicate The predicate to be called when exception of specified type happened in previous attempt.
* It should return true if another attempt is needed.
* @return this
*/
@SuppressWarnings("unchecked")
public AttemptStrategyWithRetryOnResult retryIfException(@Nonnull Class exceptionClass, @Nonnull Predicate exceptionPredicate) {
return (AttemptStrategyWithRetryOnResult) super.retryIfException(exceptionClass, exceptionPredicate);
}
/**
* Add a predicate that if a specific type of exception happened during an attempt, there needs to be a next attempt.
* retryIf*Exceptin(...) methods can be called multiple times, all the predicates will be or-ed.
* If no retryIf*Exceptin(...) has been called or if an exception happened during an attempt cannot
* make any of the predicates true, it will be propageted and there will be no further attempt.
* @param exceptionClass type of the exception that will cause next attempt.
* exceptionClass.isAssignableFrom(...) will be used to decide whether an exception is of this type.
* @return this
*/
@SuppressWarnings("unchecked")
public AttemptStrategyWithRetryOnResult retryIfException(@Nonnull Class extends Exception> exceptionClass) {
return (AttemptStrategyWithRetryOnResult) super.retryIfException(exceptionClass);
}
/**
* Add a predicate that if any RuntimeException happened during an attempt, there needs to be a next attempt.
* retryIf*Exceptin(...) methods can be called multiple times, all the predicates will be or-ed.
* If no retryIf*Exceptin(...) has been called or if an exception happened during an attempt cannot
* make any of the predicates true, it will be propageted and there will be no further attempt.
* @return this
*/
@SuppressWarnings("unchecked")
public AttemptStrategyWithRetryOnResult retryIfRuntimeException() {
return (AttemptStrategyWithRetryOnResult) super.retryIfRuntimeException();
}
/**
* Add a predicate that if any exception happened during an attempt, there needs to be a next attempt.
* retryIf*Exceptin(...) methods can be called multiple times, all the predicates will be or-ed.
* If no retryIf*Exceptin(...) has been called or if an exception happened during an attempt cannot
* make any of the predicates true, it will be propageted and there will be no further attempt.
* @return this
*/
@SuppressWarnings("unchecked")
public AttemptStrategyWithRetryOnResult retryIfException() {
return (AttemptStrategyWithRetryOnResult) super.retryIfException();
}
/**
* Adds a predicate to decide whether next attempt is needed when there is a result from previous attempt.
* retryIf*Result*(...) methods can be called multiple times, all the predicates will be or-ed.
* If no retryIf*Result*(...) method has been called or if a result got from an attempt cannot
* make any of the predicates true, it will be returned and there will be no further attempt.
* @param resultPredicate The predicate to be called when a result was returned in previous attempt.
* It should return true if another attempt is needed.
* @return this
*/
public AttemptStrategyWithRetryOnResult retryIfAttemptHasResult(@Nonnull Predicate> resultPredicate) {
Preconditions.checkNotNull(resultPredicate, "resultPredicate may not be null");
retryConditionOnResult = retryConditionOnResult == null ? resultPredicate
: retryConditionOnResult.or(resultPredicate);
return this;
}
/**
* Adds a predicate to decide whether next attempt is needed when there is a result from previous attempt.
* retryIf*Result*(...) methods can be called multiple times, all the predicates will be or-ed.
* If no retryIf*Result*(...) method has been called or if a result got from an attempt cannot
* make any of the predicates true, it will be returned and there will be no further attempt.
* @param resultPredicate The predicate to be called when a result was returned in previous attempt.
* It should return true if another attempt is needed.
* @return this
*/
public AttemptStrategyWithRetryOnResult retryIfResult(@Nonnull Predicate resultPredicate) {
Preconditions.checkNotNull(resultPredicate, "resultPredicate may not be null");
return retryIfAttemptHasResult(attempt->resultPredicate.test(attempt.getResult()));
}
/**
* Adds a predicate to decide whether next attempt is needed when a specified value equals to the result from previous attempt.
* retryIf*Result*(...) methods can be called multiple times, all the predicates will be or-ed.
* If no retryIf*Result*(...) method has been called or if a result got from an attempt cannot
* make any of the predicates true, it will be returned and there will be no further attempt.
* @param resultValue The value to be compared when a result was returned in previous attempt.
* resultValue.equals(...) will be used.
* @return this
*/
public AttemptStrategyWithRetryOnResult retryIfResultEquals(@Nonnull R resultValue) {
Preconditions.checkNotNull(resultValue, "resultValue may not be null");
return retryIfAttemptHasResult(attempt->resultValue.equals(attempt.getResult()));
}
/**
* Adds a predicate to make sure that if there is a null result from an attempt there will be a need for next attempt.
* retryIf*Result*(...) methods can be called multiple times, all the predicates will be or-ed.
* If no retryIf*Result*(...) method has been called or if a result got from an attempt cannot
* make any of the predicates true, it will be returned and there will be no further attempt.
* @return this
*/
public AttemptStrategyWithRetryOnResult retryIfResultIsNull() {
return retryIfAttemptHasResult(attempt-> null == attempt.getResult());
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy