PerExecutionDelayBuilder.java

  1. package fr.sii.ogham.core.builder.retry;

  2. import java.util.Arrays;
  3. import java.util.List;

  4. import fr.sii.ogham.core.builder.Builder;
  5. import fr.sii.ogham.core.builder.configuration.ConfigurationValueBuilder;
  6. import fr.sii.ogham.core.builder.configuration.ConfigurationValueBuilderHelper;
  7. import fr.sii.ogham.core.builder.configurer.Configurer;
  8. import fr.sii.ogham.core.builder.context.BuildContext;
  9. import fr.sii.ogham.core.builder.env.EnvironmentBuilder;
  10. import fr.sii.ogham.core.fluent.AbstractParent;
  11. import fr.sii.ogham.core.retry.PerExecutionDelayRetry;
  12. import fr.sii.ogham.core.retry.RetryStrategy;

  13. /**
  14.  * Configures retry handling based on a specific delay for each execution.
  15.  *
  16.  * Retry several times with a fixed delay to wait after the last execution
  17.  * failure until the maximum attempts is reached. A specific delay is used for
  18.  * each execution. If there are more attempts than the configured delays, the
  19.  * last delays is used for remaining attempts.
  20.  *
  21.  *
  22.  * For example:
  23.  *
  24.  * <pre>
  25.  *    .delays(500, 750, 1800)
  26.  *    .maxRetries(5)
  27.  * </pre>
  28.  *
  29.  * Means that a retry will be attempted with specified delays until 5 attempts
  30.  * are reached (inclusive). For example, you want to connect to an external
  31.  * system at t1=0 and the connection timeout (100ms) is triggered at t2=100ms.
  32.  * Using this retry will provide the following behavior:
  33.  *
  34.  * <ul>
  35.  * <li>0: connect</li>
  36.  * <li>100: timeout</li>
  37.  * <li>600: connect</li>
  38.  * <li>700: timeout</li>
  39.  * <li>1450: connect</li>
  40.  * <li>1550: timeout</li>
  41.  * <li>3350: connect</li>
  42.  * <li>3450: timeout</li>
  43.  * <li>5250: connect</li>
  44.  * <li>5350: timeout</li>
  45.  * <li>7150: connect</li>
  46.  * <li>7250: timeout</li>
  47.  * <li>fail</li>
  48.  * </ul>
  49.  *
  50.  * @author AurĂ©lien Baudet
  51.  *
  52.  * @param <P>
  53.  *            the type of the parent builder (when calling {@link #and()}
  54.  *            method)
  55.  */
  56. public class PerExecutionDelayBuilder<P> extends AbstractParent<P> implements Builder<RetryStrategy> {
  57.     private final BuildContext buildContext;
  58.     private final ConfigurationValueBuilderHelper<PerExecutionDelayBuilder<P>, Integer> maxRetriesValueBuilder;
  59.     private final ConfigurationValueBuilderHelper<PerExecutionDelayBuilder<P>, Long[]> delaysValueBuilder;

  60.     /**
  61.      * Initializes the builder with a parent builder. The parent builder is used
  62.      * when calling {@link #and()} method. The {@link EnvironmentBuilder} is
  63.      * used to evaluate properties when {@link #build()} method is called.
  64.      *
  65.      * @param parent
  66.      *            the parent builder
  67.      * @param buildContext
  68.      *            for registering instances and property evaluation
  69.      */
  70.     public PerExecutionDelayBuilder(P parent, BuildContext buildContext) {
  71.         super(parent);
  72.         this.buildContext = buildContext;
  73.         maxRetriesValueBuilder = buildContext.newConfigurationValueBuilder(this, Integer.class);
  74.         delaysValueBuilder = buildContext.newConfigurationValueBuilder(this, Long[].class);
  75.     }

  76.     /**
  77.      * Set the maximum number of attempts.
  78.      *
  79.      * <p>
  80.      * The value set using this method takes precedence over any property and
  81.      * default value configured using {@link #maxRetries()}.
  82.      *
  83.      * <pre>
  84.      * .maxRetries(10)
  85.      * .maxRetries()
  86.      *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
  87.      *   .defaultValue(5)
  88.      * </pre>
  89.      *
  90.      * <pre>
  91.      * .maxRetries(10)
  92.      * .maxRetries()
  93.      *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
  94.      *   .defaultValue(5)
  95.      * </pre>
  96.      *
  97.      * In both cases, {@code maxRetries(10)} is used.
  98.      *
  99.      * <p>
  100.      * If this method is called several times, only the last value is used.
  101.      *
  102.      * <p>
  103.      * If {@code null} value is set, it is like not setting a value at all. The
  104.      * property/default value configuration is applied.
  105.      *
  106.      * @param maxRetries
  107.      *            the maximum number of attempts
  108.      * @return this instance for fluent chaining
  109.      */
  110.     public PerExecutionDelayBuilder<P> maxRetries(Integer maxRetries) {
  111.         this.maxRetriesValueBuilder.setValue(maxRetries);
  112.         return this;
  113.     }

  114.     /**
  115.      * Set the maximum number of attempts.
  116.      *
  117.      * <p>
  118.      * This method is mainly used by {@link Configurer}s to register some
  119.      * property keys and/or a default value. The aim is to let developer be able
  120.      * to externalize its configuration (using system properties, configuration
  121.      * file or anything else). If the developer doesn't configure any value for
  122.      * the registered properties, the default value is used (if set).
  123.      *
  124.      * <pre>
  125.      * .maxRetries()
  126.      *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
  127.      *   .defaultValue(5)
  128.      * </pre>
  129.      *
  130.      * <p>
  131.      * Non-null value set using {@link #maxRetries(Integer)} takes precedence
  132.      * over property values and default value.
  133.      *
  134.      * <pre>
  135.      * .maxRetries(10)
  136.      * .maxRetries()
  137.      *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
  138.      *   .defaultValue(5)
  139.      * </pre>
  140.      *
  141.      * The value {@code 10} is used regardless of the value of the properties
  142.      * and default value.
  143.      *
  144.      * <p>
  145.      * See {@link ConfigurationValueBuilder} for more information.
  146.      *
  147.      *
  148.      * @return the builder to configure property keys/default value
  149.      */
  150.     public ConfigurationValueBuilder<PerExecutionDelayBuilder<P>, Integer> maxRetries() {
  151.         return maxRetriesValueBuilder;
  152.     }

  153.     /**
  154.      * Set specific delays (in milliseconds) used for each execution. If there
  155.      * are more attempts than the configured delays, the last delays is used for
  156.      * remaining attempts.
  157.      *
  158.      * <p>
  159.      * The value set using this method takes precedence over any property and
  160.      * default value configured using {@link #delays()}.
  161.      *
  162.      * <pre>
  163.      * .delays(5000L)
  164.      * .delays()
  165.      *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
  166.      *   .defaultValue(10000L)
  167.      * </pre>
  168.      *
  169.      * <pre>
  170.      * .delays(5000L)
  171.      * .delays()
  172.      *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
  173.      *   .defaultValue(10000L)
  174.      * </pre>
  175.      *
  176.      * In both cases, {@code delays(5000L)} is used.
  177.      *
  178.      * <p>
  179.      * If this method is called several times, only the last value is used.
  180.      *
  181.      * <p>
  182.      * If {@code null} value is set, it is like not setting a value at all. The
  183.      * property/default value configuration is applied.
  184.      *
  185.      * @param delays
  186.      *            the delays for each execution
  187.      * @return this instance for fluent chaining
  188.      */
  189.     public PerExecutionDelayBuilder<P> delays(List<Long> delays) {
  190.         delaysValueBuilder.setValue(delays == null ? null : delays.toArray(new Long[delays.size()]));
  191.         return this;
  192.     }

  193.     /**
  194.      * Set specific delays (in milliseconds) used for each execution. If there
  195.      * are more attempts than the configured delays, the last delays is used for
  196.      * remaining attempts.
  197.      *
  198.      * <p>
  199.      * The value set using this method takes precedence over any property and
  200.      * default value configured using {@link #delays()}.
  201.      *
  202.      * <pre>
  203.      * .delays(5000L)
  204.      * .delays()
  205.      *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
  206.      *   .defaultValue(10000L)
  207.      * </pre>
  208.      *
  209.      * <pre>
  210.      * .delays(5000L)
  211.      * .delays()
  212.      *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
  213.      *   .defaultValue(10000L)
  214.      * </pre>
  215.      *
  216.      * In both cases, {@code delays(5000L)} is used.
  217.      *
  218.      * <p>
  219.      * If this method is called several times, only the last value is used.
  220.      *
  221.      * <p>
  222.      * If {@code null} value is set, it is like not setting a value at all. The
  223.      * property/default value configuration is applied.
  224.      *
  225.      * @param delays
  226.      *            the delays for each execution
  227.      * @return this instance for fluent chaining
  228.      */
  229.     public PerExecutionDelayBuilder<P> delays(Long... delays) {
  230.         delaysValueBuilder.setValue(delays);
  231.         return this;
  232.     }

  233.     /**
  234.      * Set specific delays (in milliseconds) used for each new execution. If
  235.      * there are more attempts than the configured delays, the last delay is
  236.      * used for remaining attempts.
  237.      *
  238.      * <p>
  239.      * This method is mainly used by {@link Configurer}s to register some
  240.      * property keys and/or a default value. The aim is to let developer be able
  241.      * to externalize its configuration (using system properties, configuration
  242.      * file or anything else). If the developer doesn't configure any value for
  243.      * the registered properties, the default value is used (if set).
  244.      *
  245.      * <pre>
  246.      * .delays()
  247.      *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
  248.      *   .defaultValue(10000L)
  249.      * </pre>
  250.      *
  251.      * <p>
  252.      * Non-null value set using {@link #delays(Long...)} takes precedence over
  253.      * property values and default value.
  254.      *
  255.      * <pre>
  256.      * .delays(5000L)
  257.      * .delays()
  258.      *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
  259.      *   .defaultValue(10000L)
  260.      * </pre>
  261.      *
  262.      * The value {@code 5000L} is used regardless of the value of the properties
  263.      * and default value.
  264.      *
  265.      * <p>
  266.      * See {@link ConfigurationValueBuilder} for more information.
  267.      *
  268.      *
  269.      * @return the builder to configure property keys/default value
  270.      */
  271.     public ConfigurationValueBuilder<PerExecutionDelayBuilder<P>, Long[]> delays() {
  272.         return delaysValueBuilder;
  273.     }

  274.     @Override
  275.     public RetryStrategy build() {
  276.         int evaluatedMaxRetries = buildMaxRetries();
  277.         List<Long> evaluatedDelays = buildDelays();
  278.         if (evaluatedMaxRetries == 0 || evaluatedDelays.isEmpty()) {
  279.             return null;
  280.         }
  281.         return buildContext.register(new PerExecutionDelayRetry(evaluatedMaxRetries, evaluatedDelays));
  282.     }

  283.     private int buildMaxRetries() {
  284.         return maxRetriesValueBuilder.getValue(0);
  285.     }

  286.     private List<Long> buildDelays() {
  287.         Long[] delays = delaysValueBuilder.getValue(new Long[0]);
  288.         return Arrays.asList(delays);
  289.     }
  290. }