SimpleRetryExecutor.java

1
package fr.sii.ogham.core.retry;
2
3
import java.time.Instant;
4
import java.util.ArrayList;
5
import java.util.List;
6
import java.util.concurrent.Callable;
7
import java.util.function.Predicate;
8
9
import org.slf4j.Logger;
10
import org.slf4j.LoggerFactory;
11
12
import fr.sii.ogham.core.async.Awaiter;
13
import fr.sii.ogham.core.exception.async.WaitException;
14
import fr.sii.ogham.core.exception.retry.ExecutionFailedNotRetriedException;
15
import fr.sii.ogham.core.exception.retry.ExecutionFailureWrapper;
16
import fr.sii.ogham.core.exception.retry.MaximumAttemptsReachedException;
17
import fr.sii.ogham.core.exception.retry.RetryException;
18
import fr.sii.ogham.core.exception.retry.RetryExecutionInterruptedException;
19
import fr.sii.ogham.core.exception.retry.UnrecoverableException;
20
21
/**
22
 * A simple implementation that tries to execute the action, if it fails (any
23
 * exception), it waits using {@link Thread#sleep(long)}. Once the sleep is
24
 * expired, the action is executed again.
25
 * 
26
 * This process is executed until the retry strategy tells that the retries
27
 * should stop. Once stopped, it means that no execution of the action succeeded
28
 * so the last exception is thrown.
29
 * 
30
 * @author Aurélien Baudet
31
 *
32
 */
33
public class SimpleRetryExecutor implements RetryExecutor {
34
	private static final Logger LOG = LoggerFactory.getLogger(SimpleRetryExecutor.class);
35
36
	/**
37
	 * Use a provider in order to use a fresh {@link RetryStrategy} strategy
38
	 * each time the execute method is called. This is mandatory to be able to
39
	 * use the {@link RetryExecutor} in a multi-threaded application. This
40
	 * avoids sharing same instance between several {@link #execute(Callable)}
41
	 * calls.
42
	 */
43
	private final RetryStrategyProvider retryProvider;
44
45
	/**
46
	 * Implementation that waits for some time between retries
47
	 */
48
	private final Awaiter awaiter;
49
50
	/**
51
	 * Use to check if the exception is recoverable (means that a retry can be
52
	 * attempted) or not (should fail immediately).
53
	 */
54
	private final Predicate<Throwable> recoverable;
55
56
	/**
57
	 * Initializes with a provider in order to use a fresh {@link RetryStrategy}
58
	 * strategy each time the execute method is called. This is mandatory to be
59
	 * able to use the {@link RetryExecutor} in a multi-threaded application.
60
	 * This avoids sharing same instance between several
61
	 * {@link #execute(Callable)} calls.
62
	 * Every exception is considered as recoverable (means that retry is attempted).
63
	 * 
64
	 * @param retryProvider
65
	 *            the provider that will provide the retry strategy
66
	 * @param awaiter
67
	 *            the waiter that waits some time between retries
68
	 */
69
	public SimpleRetryExecutor(RetryStrategyProvider retryProvider, Awaiter awaiter) {
70 2 1. lambda$new$0 : replaced boolean return with false for fr/sii/ogham/core/retry/SimpleRetryExecutor::lambda$new$0 → NO_COVERAGE
2. lambda$new$0 : replaced boolean return with false for fr/sii/ogham/core/retry/SimpleRetryExecutor::lambda$new$0 → KILLED
		this(retryProvider, awaiter, e -> true);
71
	}
72
73
	/**
74
	 * Initializes with a provider in order to use a fresh {@link RetryStrategy}
75
	 * strategy each time the execute method is called. This is mandatory to be
76
	 * able to use the {@link RetryExecutor} in a multi-threaded application.
77
	 * This avoids sharing same instance between several
78
	 * {@link #execute(Callable)} calls.
79
	 * 
80
	 * @param retryProvider
81
	 *            the provider that will provide the retry strategy
82
	 * @param awaiter
83
	 *            the waiter that waits some time between retries
84
	 * @param recoverable
85
	 *            check if the exception is recoverable (means that retry can be
86
	 *            attempted) or unrecoverable (means that it should fail
87
	 *            immediately)
88
	 */
89
	public SimpleRetryExecutor(RetryStrategyProvider retryProvider, Awaiter awaiter, Predicate<Throwable> recoverable) {
90
		super();
91
		this.retryProvider = retryProvider;
92
		this.awaiter = awaiter;
93
		this.recoverable = recoverable;
94
	}
95
96
	@Override
97
	public <V> V execute(Callable<V> actionToRetry) throws RetryException {
98
		// new instance for each execution
99
		RetryStrategy retry = retryProvider.provide();
100 5 1. execute : negated conditional → NO_COVERAGE
2. execute : negated conditional → TIMED_OUT
3. execute : negated conditional → KILLED
4. execute : negated conditional → KILLED
5. execute : negated conditional → KILLED
		if (retry == null) {
101 3 1. execute : replaced return value with null for fr/sii/ogham/core/retry/SimpleRetryExecutor::execute → SURVIVED
2. execute : replaced return value with null for fr/sii/ogham/core/retry/SimpleRetryExecutor::execute → NO_COVERAGE
3. execute : replaced return value with null for fr/sii/ogham/core/retry/SimpleRetryExecutor::execute → KILLED
			return executeWithoutRetry(actionToRetry);
102
		}
103 5 1. execute : replaced return value with null for fr/sii/ogham/core/retry/SimpleRetryExecutor::execute → NO_COVERAGE
2. execute : replaced return value with null for fr/sii/ogham/core/retry/SimpleRetryExecutor::execute → TIMED_OUT
3. execute : replaced return value with null for fr/sii/ogham/core/retry/SimpleRetryExecutor::execute → KILLED
4. execute : replaced return value with null for fr/sii/ogham/core/retry/SimpleRetryExecutor::execute → KILLED
5. execute : replaced return value with null for fr/sii/ogham/core/retry/SimpleRetryExecutor::execute → KILLED
		return executeWithRetry(actionToRetry, retry);
104
	}
105
106
	private <V> V executeWithRetry(Callable<V> actionToRetry, RetryStrategy retry) throws RetryExecutionInterruptedException, MaximumAttemptsReachedException, UnrecoverableException {
107
		List<Exception> failures = new ArrayList<>();
108
		do {
109
			Instant executionStartTime = Instant.now();
110
			try {
111 5 1. executeWithRetry : replaced return value with null for fr/sii/ogham/core/retry/SimpleRetryExecutor::executeWithRetry → NO_COVERAGE
2. executeWithRetry : replaced return value with null for fr/sii/ogham/core/retry/SimpleRetryExecutor::executeWithRetry → TIMED_OUT
3. executeWithRetry : replaced return value with null for fr/sii/ogham/core/retry/SimpleRetryExecutor::executeWithRetry → KILLED
4. executeWithRetry : replaced return value with null for fr/sii/ogham/core/retry/SimpleRetryExecutor::executeWithRetry → KILLED
5. executeWithRetry : replaced return value with null for fr/sii/ogham/core/retry/SimpleRetryExecutor::executeWithRetry → KILLED
				return actionToRetry.call();
112
			} catch (Exception e) {
113
				Instant executionFailure = Instant.now();
114 4 1. executeWithRetry : removed call to fr/sii/ogham/core/retry/SimpleRetryExecutor::handleFailure → NO_COVERAGE
2. executeWithRetry : removed call to fr/sii/ogham/core/retry/SimpleRetryExecutor::handleFailure → KILLED
3. executeWithRetry : removed call to fr/sii/ogham/core/retry/SimpleRetryExecutor::handleFailure → KILLED
4. executeWithRetry : removed call to fr/sii/ogham/core/retry/SimpleRetryExecutor::handleFailure → KILLED
				handleFailure(executionStartTime, executionFailure, actionToRetry, failures, e);
115 3 1. executeWithRetry : removed call to fr/sii/ogham/core/retry/SimpleRetryExecutor::pause → NO_COVERAGE
2. executeWithRetry : removed call to fr/sii/ogham/core/retry/SimpleRetryExecutor::pause → TIMED_OUT
3. executeWithRetry : removed call to fr/sii/ogham/core/retry/SimpleRetryExecutor::pause → KILLED
				pause(executionStartTime, executionFailure, actionToRetry, retry, e);
116
			}
117 4 1. executeWithRetry : negated conditional → NO_COVERAGE
2. executeWithRetry : negated conditional → TIMED_OUT
3. executeWithRetry : negated conditional → KILLED
4. executeWithRetry : negated conditional → KILLED
		} while (!retry.terminated());
118
		// action couldn't be executed
119
		throw new MaximumAttemptsReachedException("Maximum attempts to execute action '" + getActionName(actionToRetry) + "' is reached", failures);
120
	}
121
122
	private <V> void handleFailure(Instant executionStart, Instant executionFailure, Callable<V> actionToRetry, List<Exception> failures, Exception e) throws UnrecoverableException {
123
		failures.add(new ExecutionFailureWrapper(getActionName(actionToRetry), executionStart, executionFailure, e));
124 4 1. handleFailure : negated conditional → NO_COVERAGE
2. handleFailure : negated conditional → KILLED
3. handleFailure : negated conditional → KILLED
4. handleFailure : negated conditional → KILLED
		if (!recoverable.test(e)) {
125
			throw new UnrecoverableException("Unrecoverable exception thrown while executing '" + getActionName(actionToRetry) + "'", failures);
126
		}
127
	}
128
129
	private static <V> V executeWithoutRetry(Callable<V> actionToRetry) throws ExecutionFailedNotRetriedException {
130
		try {
131 3 1. executeWithoutRetry : replaced return value with null for fr/sii/ogham/core/retry/SimpleRetryExecutor::executeWithoutRetry → NO_COVERAGE
2. executeWithoutRetry : replaced return value with null for fr/sii/ogham/core/retry/SimpleRetryExecutor::executeWithoutRetry → SURVIVED
3. executeWithoutRetry : replaced return value with null for fr/sii/ogham/core/retry/SimpleRetryExecutor::executeWithoutRetry → KILLED
			return actionToRetry.call();
132
		} catch (Exception e) {
133
			throw new ExecutionFailedNotRetriedException("Failed to execute action '" + getActionName(actionToRetry) + "' and no retry strategy configured", e);
134
		}
135
	}
136
137
	private <V> void pause(Instant executionStartTime, Instant executionFailureTime, Callable<V> actionToRetry, RetryStrategy retry, Exception e) throws RetryExecutionInterruptedException {
138
		Instant nextDate = retry.nextDate(executionStartTime, executionFailureTime);
139
		LOG.debug("{} failed ({}: {}). Retrying at {}...", getActionName(actionToRetry), e.getClass(), e.getMessage(), nextDate);
140
		LOG.trace("{}", e.getMessage(), e);
141 3 1. pause : removed call to fr/sii/ogham/core/retry/SimpleRetryExecutor::pauseUntil → NO_COVERAGE
2. pause : removed call to fr/sii/ogham/core/retry/SimpleRetryExecutor::pauseUntil → SURVIVED
3. pause : removed call to fr/sii/ogham/core/retry/SimpleRetryExecutor::pauseUntil → KILLED
		pauseUntil(nextDate);
142
	}
143
144
	private void pauseUntil(Instant nextDate) throws RetryExecutionInterruptedException {
145
		try {
146 3 1. pauseUntil : removed call to fr/sii/ogham/core/async/Awaiter::waitUntil → NO_COVERAGE
2. pauseUntil : removed call to fr/sii/ogham/core/async/Awaiter::waitUntil → SURVIVED
3. pauseUntil : removed call to fr/sii/ogham/core/async/Awaiter::waitUntil → KILLED
			awaiter.waitUntil(nextDate);
147
		} catch (WaitException e) {
148
			throw new RetryExecutionInterruptedException(e);
149
		}
150
	}
151
152
	private static <V> String getActionName(Callable<V> actionToRetry) {
153 3 1. getActionName : negated conditional → SURVIVED
2. getActionName : negated conditional → NO_COVERAGE
3. getActionName : negated conditional → KILLED
		if (actionToRetry instanceof NamedCallable) {
154 2 1. getActionName : replaced return value with "" for fr/sii/ogham/core/retry/SimpleRetryExecutor::getActionName → NO_COVERAGE
2. getActionName : replaced return value with "" for fr/sii/ogham/core/retry/SimpleRetryExecutor::getActionName → SURVIVED
			return ((NamedCallable<?>) actionToRetry).getName();
155
		}
156 2 1. getActionName : replaced return value with "" for fr/sii/ogham/core/retry/SimpleRetryExecutor::getActionName → SURVIVED
2. getActionName : replaced return value with "" for fr/sii/ogham/core/retry/SimpleRetryExecutor::getActionName → NO_COVERAGE
		return "unnamed";
157
	}
158
159
}

Mutations

70

1.1
Location : lambda$new$0
Killed by : none
replaced boolean return with false for fr/sii/ogham/core/retry/SimpleRetryExecutor::lambda$new$0 → NO_COVERAGE

2.2
Location : lambda$new$0
Killed by : oghamcore.ut.core.retry.SimpleRetryExecutorTest.thrirdInvocationWorks(oghamcore.ut.core.retry.SimpleRetryExecutorTest)
replaced boolean return with false for fr/sii/ogham/core/retry/SimpleRetryExecutor::lambda$new$0 → KILLED

100

1.1
Location : execute
Killed by : none
negated conditional → TIMED_OUT

2.2
Location : execute
Killed by : none
negated conditional → NO_COVERAGE

3.3
Location : execute
Killed by : oghamcore.ut.core.retry.SimpleRetryExecutorTest.thrirdInvocationWorks(oghamcore.ut.core.retry.SimpleRetryExecutorTest)
negated conditional → KILLED

4.4
Location : execute
Killed by : oghamall.it.retry.AutoRetryTest.doNotResendEmailIfTemplateNotFound(oghamall.it.retry.AutoRetryTest)
negated conditional → KILLED

5.5
Location : execute
Killed by : oghamcloudhopper.it.AutoRetryExtensionTest.smsNotRetriedDueToCloudhopperError(oghamcloudhopper.it.AutoRetryExtensionTest)
negated conditional → KILLED

101

1.1
Location : execute
Killed by : none
replaced return value with null for fr/sii/ogham/core/retry/SimpleRetryExecutor::execute → SURVIVED

2.2
Location : execute
Killed by : oghamcloudhopper.it.PartialConfigurationTest.nothingConfiguredAndLongMessageShouldSendOneLongMessageUsingDefaultEncoding(oghamcloudhopper.it.PartialConfigurationTest)
replaced return value with null for fr/sii/ogham/core/retry/SimpleRetryExecutor::execute → KILLED

3.3
Location : execute
Killed by : none
replaced return value with null for fr/sii/ogham/core/retry/SimpleRetryExecutor::execute → NO_COVERAGE

103

1.1
Location : execute
Killed by : oghamcore.ut.core.retry.SimpleRetryExecutorTest.thrirdInvocationWorks(oghamcore.ut.core.retry.SimpleRetryExecutorTest)
replaced return value with null for fr/sii/ogham/core/retry/SimpleRetryExecutor::execute → KILLED

2.2
Location : execute
Killed by : none
replaced return value with null for fr/sii/ogham/core/retry/SimpleRetryExecutor::execute → TIMED_OUT

3.3
Location : execute
Killed by : none
replaced return value with null for fr/sii/ogham/core/retry/SimpleRetryExecutor::execute → NO_COVERAGE

4.4
Location : execute
Killed by : oghamcloudhopper.it.SpecialCharactersTest.gsm8bit(oghamcloudhopper.it.SpecialCharactersTest)
replaced return value with null for fr/sii/ogham/core/retry/SimpleRetryExecutor::execute → KILLED

5.5
Location : execute
Killed by : oghamall.it.sms.SmsSMPPGsm7bitTest.longMessage(oghamall.it.sms.SmsSMPPGsm7bitTest)
replaced return value with null for fr/sii/ogham/core/retry/SimpleRetryExecutor::execute → KILLED

111

1.1
Location : executeWithRetry
Killed by : oghamcore.ut.core.retry.SimpleRetryExecutorTest.thrirdInvocationWorks(oghamcore.ut.core.retry.SimpleRetryExecutorTest)
replaced return value with null for fr/sii/ogham/core/retry/SimpleRetryExecutor::executeWithRetry → KILLED

2.2
Location : executeWithRetry
Killed by : none
replaced return value with null for fr/sii/ogham/core/retry/SimpleRetryExecutor::executeWithRetry → NO_COVERAGE

3.3
Location : executeWithRetry
Killed by : none
replaced return value with null for fr/sii/ogham/core/retry/SimpleRetryExecutor::executeWithRetry → TIMED_OUT

4.4
Location : executeWithRetry
Killed by : oghamcloudhopper.it.SpecialCharactersTest.gsm8bit(oghamcloudhopper.it.SpecialCharactersTest)
replaced return value with null for fr/sii/ogham/core/retry/SimpleRetryExecutor::executeWithRetry → KILLED

5.5
Location : executeWithRetry
Killed by : oghamall.it.sms.SmsSMPPGsm7bitTest.longMessage(oghamall.it.sms.SmsSMPPGsm7bitTest)
replaced return value with null for fr/sii/ogham/core/retry/SimpleRetryExecutor::executeWithRetry → KILLED

114

1.1
Location : executeWithRetry
Killed by : none
removed call to fr/sii/ogham/core/retry/SimpleRetryExecutor::handleFailure → NO_COVERAGE

2.2
Location : executeWithRetry
Killed by : oghamcloudhopper.it.AutoRetryExtensionTest.smsNotRetriedDueToCloudhopperError(oghamcloudhopper.it.AutoRetryExtensionTest)
removed call to fr/sii/ogham/core/retry/SimpleRetryExecutor::handleFailure → KILLED

3.3
Location : executeWithRetry
Killed by : oghamall.it.retry.AutoRetryTest.doNotResendEmailIfTemplateNotFound(oghamall.it.retry.AutoRetryTest)
removed call to fr/sii/ogham/core/retry/SimpleRetryExecutor::handleFailure → KILLED

4.4
Location : executeWithRetry
Killed by : oghamcore.it.core.sender.AutoRetryTest.smsNotRetriedOnFirstExecutionDueToParsingError(oghamcore.it.core.sender.AutoRetryTest)
removed call to fr/sii/ogham/core/retry/SimpleRetryExecutor::handleFailure → KILLED

115

1.1
Location : executeWithRetry
Killed by : none
removed call to fr/sii/ogham/core/retry/SimpleRetryExecutor::pause → NO_COVERAGE

2.2
Location : executeWithRetry
Killed by : none
removed call to fr/sii/ogham/core/retry/SimpleRetryExecutor::pause → TIMED_OUT

3.3
Location : executeWithRetry
Killed by : oghamcore.ut.core.retry.SimpleRetryExecutorTest.thrirdInvocationWorks(oghamcore.ut.core.retry.SimpleRetryExecutorTest)
removed call to fr/sii/ogham/core/retry/SimpleRetryExecutor::pause → KILLED

117

1.1
Location : executeWithRetry
Killed by : none
negated conditional → TIMED_OUT

2.2
Location : executeWithRetry
Killed by : none
negated conditional → NO_COVERAGE

3.3
Location : executeWithRetry
Killed by : oghamcore.ut.core.retry.SimpleRetryExecutorTest.thrirdInvocationWorks(oghamcore.ut.core.retry.SimpleRetryExecutorTest)
negated conditional → KILLED

4.4
Location : executeWithRetry
Killed by : oghamall.it.retry.AutoRetryTest.resendSms(oghamall.it.retry.AutoRetryTest)
negated conditional → KILLED

124

1.1
Location : handleFailure
Killed by : oghamall.it.retry.AutoRetryTest.doNotResendEmailIfTemplateNotFound(oghamall.it.retry.AutoRetryTest)
negated conditional → KILLED

2.2
Location : handleFailure
Killed by : oghamcloudhopper.it.AutoRetryExtensionTest.smsNotRetriedDueToCloudhopperError(oghamcloudhopper.it.AutoRetryExtensionTest)
negated conditional → KILLED

3.3
Location : handleFailure
Killed by : none
negated conditional → NO_COVERAGE

4.4
Location : handleFailure
Killed by : oghamcore.ut.core.retry.SimpleRetryExecutorTest.thrirdInvocationWorks(oghamcore.ut.core.retry.SimpleRetryExecutorTest)
negated conditional → KILLED

131

1.1
Location : executeWithoutRetry
Killed by : none
replaced return value with null for fr/sii/ogham/core/retry/SimpleRetryExecutor::executeWithoutRetry → NO_COVERAGE

2.2
Location : executeWithoutRetry
Killed by : oghamcloudhopper.it.PartialConfigurationTest.nothingConfiguredAndLongMessageShouldSendOneLongMessageUsingDefaultEncoding(oghamcloudhopper.it.PartialConfigurationTest)
replaced return value with null for fr/sii/ogham/core/retry/SimpleRetryExecutor::executeWithoutRetry → KILLED

3.3
Location : executeWithoutRetry
Killed by : none
replaced return value with null for fr/sii/ogham/core/retry/SimpleRetryExecutor::executeWithoutRetry → SURVIVED

141

1.1
Location : pause
Killed by : none
removed call to fr/sii/ogham/core/retry/SimpleRetryExecutor::pauseUntil → NO_COVERAGE

2.2
Location : pause
Killed by : oghamcore.ut.core.retry.SimpleRetryExecutorTest.thrirdInvocationWorks(oghamcore.ut.core.retry.SimpleRetryExecutorTest)
removed call to fr/sii/ogham/core/retry/SimpleRetryExecutor::pauseUntil → KILLED

3.3
Location : pause
Killed by : none
removed call to fr/sii/ogham/core/retry/SimpleRetryExecutor::pauseUntil → SURVIVED

146

1.1
Location : pauseUntil
Killed by : oghamcore.ut.core.retry.SimpleRetryExecutorTest.thrirdInvocationWorks(oghamcore.ut.core.retry.SimpleRetryExecutorTest)
removed call to fr/sii/ogham/core/async/Awaiter::waitUntil → KILLED

2.2
Location : pauseUntil
Killed by : none
removed call to fr/sii/ogham/core/async/Awaiter::waitUntil → NO_COVERAGE

3.3
Location : pauseUntil
Killed by : none
removed call to fr/sii/ogham/core/async/Awaiter::waitUntil → SURVIVED

153

1.1
Location : getActionName
Killed by : oghamcore.ut.core.retry.SimpleRetryExecutorTest.thrirdInvocationWorks(oghamcore.ut.core.retry.SimpleRetryExecutorTest)
negated conditional → KILLED

2.2
Location : getActionName
Killed by : none
negated conditional → SURVIVED

3.3
Location : getActionName
Killed by : none
negated conditional → NO_COVERAGE

154

1.1
Location : getActionName
Killed by : none
replaced return value with "" for fr/sii/ogham/core/retry/SimpleRetryExecutor::getActionName → NO_COVERAGE

2.2
Location : getActionName
Killed by : none
replaced return value with "" for fr/sii/ogham/core/retry/SimpleRetryExecutor::getActionName → SURVIVED

156

1.1
Location : getActionName
Killed by : none
replaced return value with "" for fr/sii/ogham/core/retry/SimpleRetryExecutor::getActionName → SURVIVED

2.2
Location : getActionName
Killed by : none
replaced return value with "" for fr/sii/ogham/core/retry/SimpleRetryExecutor::getActionName → NO_COVERAGE

Active mutators

Tests examined


Report generated by PIT OGHAM