PerExecutionDelayBuilder.java
- package fr.sii.ogham.core.builder.retry;
- import java.util.Arrays;
- import java.util.List;
- import fr.sii.ogham.core.builder.Builder;
- import fr.sii.ogham.core.builder.configuration.ConfigurationValueBuilder;
- import fr.sii.ogham.core.builder.configuration.ConfigurationValueBuilderHelper;
- import fr.sii.ogham.core.builder.configurer.Configurer;
- import fr.sii.ogham.core.builder.context.BuildContext;
- import fr.sii.ogham.core.builder.env.EnvironmentBuilder;
- import fr.sii.ogham.core.fluent.AbstractParent;
- import fr.sii.ogham.core.retry.PerExecutionDelayRetry;
- import fr.sii.ogham.core.retry.RetryStrategy;
- /**
- * Configures retry handling based on a specific delay for each execution.
- *
- * Retry several times with a fixed delay to wait after the last execution
- * failure until the maximum attempts is reached. A specific delay is used for
- * each execution. If there are more attempts than the configured delays, the
- * last delays is used for remaining attempts.
- *
- *
- * For example:
- *
- * <pre>
- * .delays(500, 750, 1800)
- * .maxRetries(5)
- * </pre>
- *
- * Means that a retry will be attempted with specified delays 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>600: connect</li>
- * <li>700: timeout</li>
- * <li>1450: connect</li>
- * <li>1550: timeout</li>
- * <li>3350: connect</li>
- * <li>3450: timeout</li>
- * <li>5250: connect</li>
- * <li>5350: timeout</li>
- * <li>7150: connect</li>
- * <li>7250: timeout</li>
- * <li>fail</li>
- * </ul>
- *
- * @author Aurélien Baudet
- *
- * @param <P>
- * the type of the parent builder (when calling {@link #and()}
- * method)
- */
- public class PerExecutionDelayBuilder<P> extends AbstractParent<P> implements Builder<RetryStrategy> {
- private final BuildContext buildContext;
- private final ConfigurationValueBuilderHelper<PerExecutionDelayBuilder<P>, Integer> maxRetriesValueBuilder;
- private final ConfigurationValueBuilderHelper<PerExecutionDelayBuilder<P>, Long[]> delaysValueBuilder;
- /**
- * Initializes the builder with a parent builder. The parent builder is used
- * when calling {@link #and()} method. The {@link EnvironmentBuilder} is
- * used to evaluate properties when {@link #build()} method is called.
- *
- * @param parent
- * the parent builder
- * @param buildContext
- * for registering instances and property evaluation
- */
- public PerExecutionDelayBuilder(P parent, BuildContext buildContext) {
- super(parent);
- this.buildContext = buildContext;
- maxRetriesValueBuilder = buildContext.newConfigurationValueBuilder(this, Integer.class);
- delaysValueBuilder = buildContext.newConfigurationValueBuilder(this, Long[].class);
- }
- /**
- * Set the maximum number of attempts.
- *
- * <p>
- * The value set using this method takes precedence over any property and
- * default value configured using {@link #maxRetries()}.
- *
- * <pre>
- * .maxRetries(10)
- * .maxRetries()
- * .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
- * .defaultValue(5)
- * </pre>
- *
- * <pre>
- * .maxRetries(10)
- * .maxRetries()
- * .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
- * .defaultValue(5)
- * </pre>
- *
- * In both cases, {@code maxRetries(10)} is used.
- *
- * <p>
- * If this method is called several times, only the last value is used.
- *
- * <p>
- * If {@code null} value is set, it is like not setting a value at all. The
- * property/default value configuration is applied.
- *
- * @param maxRetries
- * the maximum number of attempts
- * @return this instance for fluent chaining
- */
- public PerExecutionDelayBuilder<P> maxRetries(Integer maxRetries) {
- this.maxRetriesValueBuilder.setValue(maxRetries);
- return this;
- }
- /**
- * Set the maximum number of attempts.
- *
- * <p>
- * This method is mainly used by {@link Configurer}s to register some
- * property keys and/or a default value. The aim is to let developer be able
- * to externalize its configuration (using system properties, configuration
- * file or anything else). If the developer doesn't configure any value for
- * the registered properties, the default value is used (if set).
- *
- * <pre>
- * .maxRetries()
- * .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
- * .defaultValue(5)
- * </pre>
- *
- * <p>
- * Non-null value set using {@link #maxRetries(Integer)} takes precedence
- * over property values and default value.
- *
- * <pre>
- * .maxRetries(10)
- * .maxRetries()
- * .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
- * .defaultValue(5)
- * </pre>
- *
- * The value {@code 10} is used regardless of the value of the properties
- * and default value.
- *
- * <p>
- * See {@link ConfigurationValueBuilder} for more information.
- *
- *
- * @return the builder to configure property keys/default value
- */
- public ConfigurationValueBuilder<PerExecutionDelayBuilder<P>, Integer> maxRetries() {
- return maxRetriesValueBuilder;
- }
- /**
- * Set specific delays (in milliseconds) used for each execution. If there
- * are more attempts than the configured delays, the last delays is used for
- * remaining attempts.
- *
- * <p>
- * The value set using this method takes precedence over any property and
- * default value configured using {@link #delays()}.
- *
- * <pre>
- * .delays(5000L)
- * .delays()
- * .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
- * .defaultValue(10000L)
- * </pre>
- *
- * <pre>
- * .delays(5000L)
- * .delays()
- * .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
- * .defaultValue(10000L)
- * </pre>
- *
- * In both cases, {@code delays(5000L)} is used.
- *
- * <p>
- * If this method is called several times, only the last value is used.
- *
- * <p>
- * If {@code null} value is set, it is like not setting a value at all. The
- * property/default value configuration is applied.
- *
- * @param delays
- * the delays for each execution
- * @return this instance for fluent chaining
- */
- public PerExecutionDelayBuilder<P> delays(List<Long> delays) {
- delaysValueBuilder.setValue(delays == null ? null : delays.toArray(new Long[delays.size()]));
- return this;
- }
- /**
- * Set specific delays (in milliseconds) used for each execution. If there
- * are more attempts than the configured delays, the last delays is used for
- * remaining attempts.
- *
- * <p>
- * The value set using this method takes precedence over any property and
- * default value configured using {@link #delays()}.
- *
- * <pre>
- * .delays(5000L)
- * .delays()
- * .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
- * .defaultValue(10000L)
- * </pre>
- *
- * <pre>
- * .delays(5000L)
- * .delays()
- * .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
- * .defaultValue(10000L)
- * </pre>
- *
- * In both cases, {@code delays(5000L)} is used.
- *
- * <p>
- * If this method is called several times, only the last value is used.
- *
- * <p>
- * If {@code null} value is set, it is like not setting a value at all. The
- * property/default value configuration is applied.
- *
- * @param delays
- * the delays for each execution
- * @return this instance for fluent chaining
- */
- public PerExecutionDelayBuilder<P> delays(Long... delays) {
- delaysValueBuilder.setValue(delays);
- return this;
- }
- /**
- * Set specific delays (in milliseconds) used for each new execution. If
- * there are more attempts than the configured delays, the last delay is
- * used for remaining attempts.
- *
- * <p>
- * This method is mainly used by {@link Configurer}s to register some
- * property keys and/or a default value. The aim is to let developer be able
- * to externalize its configuration (using system properties, configuration
- * file or anything else). If the developer doesn't configure any value for
- * the registered properties, the default value is used (if set).
- *
- * <pre>
- * .delays()
- * .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
- * .defaultValue(10000L)
- * </pre>
- *
- * <p>
- * Non-null value set using {@link #delays(Long...)} takes precedence over
- * property values and default value.
- *
- * <pre>
- * .delays(5000L)
- * .delays()
- * .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
- * .defaultValue(10000L)
- * </pre>
- *
- * The value {@code 5000L} is used regardless of the value of the properties
- * and default value.
- *
- * <p>
- * See {@link ConfigurationValueBuilder} for more information.
- *
- *
- * @return the builder to configure property keys/default value
- */
- public ConfigurationValueBuilder<PerExecutionDelayBuilder<P>, Long[]> delays() {
- return delaysValueBuilder;
- }
- @Override
- public RetryStrategy build() {
- int evaluatedMaxRetries = buildMaxRetries();
- List<Long> evaluatedDelays = buildDelays();
- if (evaluatedMaxRetries == 0 || evaluatedDelays.isEmpty()) {
- return null;
- }
- return buildContext.register(new PerExecutionDelayRetry(evaluatedMaxRetries, evaluatedDelays));
- }
- private int buildMaxRetries() {
- return maxRetriesValueBuilder.getValue(0);
- }
- private List<Long> buildDelays() {
- Long[] delays = delaysValueBuilder.getValue(new Long[0]);
- return Arrays.asList(delays);
- }
- }