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

io.camunda.operate.util.RetryOperation Maven / Gradle / Ivy

There is a newer version: 8.6.0-alpha5
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.operate.util;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
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 RetryConsumer retryConsumer;
  private int noOfRetry;
  private int delayInterval;
  private TimeUnit timeUnit;
  private RetryPredicate retryPredicate;
  private List> exceptionList;
  private String message;

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

  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 (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, 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++;
    if (retries < noOfRetry && delayInterval > 0) {
      try {
        if (retries % 20 == 0) {
          LOGGER.info(
              "{} - Waiting {} {}. {}/{}", message, delayInterval, timeUnit, retries, noOfRetry);
        } else {
          LOGGER.debug(
              "{} - Waiting {} {}. {}/{}", message, delayInterval, timeUnit, retries, noOfRetry);
        }
        timeUnit.sleep(delayInterval);
      } catch (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 OperationBuilder() {}

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

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

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

    public OperationBuilder retryPredicate(final RetryPredicate retryPredicate) {
      this.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 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);
    }
  }

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

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




© 2015 - 2024 Weber Informatics LLC | Privacy Policy