All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.camunda.exporter.utils.RetryOperation Maven / Gradle / Ivy

There is a newer version: 8.7.0-alpha2
Show newest version
/*
 * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH under
 * one or more contributor license agreements. See the NOTICE file distributed
 * with this work for additional information regarding copyright ownership.
 * Licensed under the Camunda License 1.0. You may not use this file
 * except in compliance with the Camunda License 1.0.
 */
package io.camunda.exporter.utils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

//
// Taken from https://stackoverflow.com/questions/13239972/how-do-you-implement-a-re-try-catch and
// slightly modified
//
public final class RetryOperation {

  private static final Logger LOGGER = LoggerFactory.getLogger(RetryOperation.class);
  private final RetryConsumer retryConsumer;
  private final int noOfRetry;
  private final int delayInterval;
  private final TimeUnit timeUnit;
  private final RetryPredicate retryPredicate;
  private final List> exceptionList;
  private final String message;
  private final Supplier delaySupplier;

  private RetryOperation(
      final RetryConsumer retryConsumer,
      final int noOfRetry,
      final int delayInterval,
      final TimeUnit timeUnit,
      final RetryPredicate retryPredicate,
      final List> exceptionList,
      final String message,
      final Supplier delaySupplier) {
    this.retryConsumer = retryConsumer;
    this.noOfRetry = noOfRetry;
    this.delayInterval = delayInterval;
    this.timeUnit = timeUnit;
    this.retryPredicate = retryPredicate;
    this.exceptionList = exceptionList;
    this.message = message;
    this.delaySupplier = delaySupplier;
  }

  public static  OperationBuilder newBuilder() {
    return new OperationBuilder<>();
  }

  public T retry() throws Exception {
    T result = null;
    int retries = 0;
    while (retries < noOfRetry) {
      try {
        result = retryConsumer.evaluate();
        if (Objects.nonNull(retryPredicate)) {
          final boolean shouldItRetry = retryPredicate.shouldRetry(result);
          if (shouldItRetry) {
            retries = increaseRetryCountAndSleep(retries);
          } else {
            return result;
          }
        } else {
          // no retry condition defined, no exception thrown. This is the desired result.
          return result;
        }
      } catch (final Exception e) {
        LOGGER.warn(String.format("Retry Operation %s failed: %s", message, e.getMessage()), e);
        retries = handleException(retries, e);
      }
    }
    return result;
  }

  private int handleException(int retries, final Exception e) throws Exception {
    if (exceptionList.isEmpty()
        || exceptionList.stream().anyMatch(ex -> ex.isAssignableFrom(e.getClass()))) {
      // exception is accepted, continue retry.
      retries = increaseRetryCountAndSleep(retries);
      if (retries == noOfRetry) {
        // evaluation is throwing exception, no more retry left. Throw it.
        throw e;
      }
    } else {
      // unexpected exception, no retry required. Throw it.
      throw e;
    }
    return retries;
  }

  private int increaseRetryCountAndSleep(int retries) {
    retries++;
    final int delay = Objects.nonNull(delaySupplier) ? delaySupplier.get() : delayInterval;
    if (retries < noOfRetry && delay > 0) {
      try {
        if (retries % 20 == 0) {
          LOGGER.info("{} - Waiting {} {}. {}/{}", message, delay, timeUnit, retries, noOfRetry);
        } else {
          LOGGER.debug("{} - Waiting {} {}. {}/{}", message, delay, timeUnit, retries, noOfRetry);
        }
        timeUnit.sleep(delay);
      } catch (final InterruptedException ignore) {
        Thread.currentThread().interrupt();
      }
    }
    return retries;
  }

  public static final class OperationBuilder {
    private RetryConsumer iRetryConsumer;
    private int iNoOfRetry;
    private int iDelayInterval;
    private TimeUnit iTimeUnit;
    private RetryPredicate iRetryPredicate;
    private Class[] exceptionClasses;
    private String message = "";
    private Supplier delaySupplier;

    private OperationBuilder() {}

    public OperationBuilder retryConsumer(final RetryConsumer retryConsumer) {
      iRetryConsumer = retryConsumer;
      return this;
    }

    public OperationBuilder noOfRetry(final int noOfRetry) {
      iNoOfRetry = noOfRetry;
      return this;
    }

    public OperationBuilder delayInterval(final int delayInterval, final TimeUnit timeUnit) {
      iDelayInterval = delayInterval;
      iTimeUnit = timeUnit;
      return this;
    }

    public OperationBuilder retryPredicate(final RetryPredicate retryPredicate) {
      iRetryPredicate = retryPredicate;
      return this;
    }

    @SafeVarargs
    public final OperationBuilder retryOn(final Class... exceptionClasses) {
      this.exceptionClasses = exceptionClasses;
      return this;
    }

    public OperationBuilder message(final String message) {
      this.message = message;
      return this;
    }

    public OperationBuilder delaySupplier(final Supplier delaySupplier) {
      this.delaySupplier = delaySupplier;
      return this;
    }

    public RetryOperation build() {
      if (Objects.isNull(iRetryConsumer)) {
        throw new RuntimeException("'#retryConsumer:RetryConsumer' not set");
      }

      List> exceptionList = new ArrayList<>();
      if (Objects.nonNull(exceptionClasses) && exceptionClasses.length > 0) {
        exceptionList = Arrays.asList(exceptionClasses);
      }
      iNoOfRetry = iNoOfRetry == 0 ? 1 : iNoOfRetry;
      iTimeUnit = Objects.isNull(iTimeUnit) ? TimeUnit.MILLISECONDS : iTimeUnit;
      return new RetryOperation<>(
          iRetryConsumer,
          iNoOfRetry,
          iDelayInterval,
          iTimeUnit,
          iRetryPredicate,
          exceptionList,
          message,
          delaySupplier);
    }
  }

  public static interface RetryConsumer {
    T evaluate() throws Exception;
  }

  public static interface RetryPredicate {
    boolean shouldRetry(T t);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy