FixedIntervalRetry.java

package fr.sii.ogham.core.retry;

import java.time.Instant;

/**
 * Retry several times with a fixed delay between each try until the maximum
 * attempts is reached. The next execution is based on the execution start date.
 * 
 * If delay is 500ms and max retries is 5, it means that a retry will be
 * attempted every 500ms until 5 attempts are reached (inclusive). For example,
 * you want to connect to an external system at t1=0 and the connection timeout
 * (100ms) is triggered at t2=100ms. Using this retry will provide the following
 * behavior:
 * 
 * <ul>
 * <li>0: connect</li>
 * <li>100: timeout</li>
 * <li>500: connect</li>
 * <li>600: timeout</li>
 * <li>1000: connect</li>
 * <li>1100: timeout</li>
 * <li>1500: connect</li>
 * <li>1600: timeout</li>
 * <li>2000: connect</li>
 * <li>2100: timeout</li>
 * <li>2500: connect</li>
 * <li>2600: timeout</li>
 * <li>fail</li>
 * </ul>
 * 
 * 
 * <strong>NOTE:</strong> The provided date doesn't take the duration of the
 * execution in account. If an execution takes 1s to execute while retry delay
 * is set to 500ms, there may have several executions in parallel. However, this
 * totally depends on the {@link RetryExecutor} implementation. For example
 * {@link SimpleRetryExecutor} won't run several executions in parallel. In this
 * case, it will execute the action as soon as the previous one has failed
 * therefore the delay may not be complied.
 * 
 * @author Aurélien Baudet
 *
 */
public class FixedIntervalRetry implements RetryStrategy {
	private final int maxRetries;
	private final long interval;
	private Instant firstExecutionTime;
	private int retries;
	private int remainingRetries;

	/**
	 * Initializes with the maximum attempts and the delay between each attempt.
	 * 
	 * @param maxRetries
	 *            the maximum attempts
	 * @param interval
	 *            the interval between attempts
	 */
	public FixedIntervalRetry(int maxRetries, long interval) {
		super();
		this.maxRetries = maxRetries;
		this.interval = interval;
		remainingRetries = maxRetries;
	}

	@Override
	public boolean terminated() {
		return remainingRetries <= 0;
	}

	@Override
	public Instant nextDate(Instant executionStartTime, Instant executionFailureTime) {
		remainingRetries--;
		retries++;
		if (firstExecutionTime == null) {
			firstExecutionTime = executionStartTime;
		}
		return firstExecutionTime.plusMillis(interval * retries);
	}

	public int getRemainingRetries() {
		return remainingRetries;
	}

	public int getMaxRetries() {
		return maxRetries;
	}

}