io.camunda.exporter.utils.RetryOperation Maven / Gradle / Ivy
/*
* 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 extends Throwable>[] 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 extends Throwable>... 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