Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java: how to handle retries without copy-paste code?

I have multiple cases when I have to deal retrial for DB and networking operations. Everywhere I do it I have the following type of code:

    for (int iteration = 1; ; iteration++) {
        try {
            data = doSomethingUseful(data);

            break;
        } catch (SomeException | AndAnotherException e) {
            if (iteration == helper.getNumberOfRetries()) {
                throw e;
            } else {
                errorReporter.reportError("Got following error for data = {}. Continue trying after delay...", data, e);
                utilities.defaultDelayForIteration(iteration);
                handleSpecificCase(data);
            }
        }
    }

The issue is that this code pattern is copy-pasted all over my classes. Which is really bad. I can't figure out how to get rid of this for-break-catch copy-paste pattern, since I usually get different exception to handle, I want to log data I failed on (usually also different ways).

Is there a good way to avoid this copy-paste in Java 7?

Edit: I do use guice for dependency injection. I do have checked exceptions. There could be multiple variables instead of just one data and they are all of different type.

Edit2: AOP approach looks as the most promising for me.

like image 546
Artem Avatar asked Mar 02 '12 20:03

Artem


People also ask

How do you retry a method in Java?

A simple solution to implement retry logic in Java is to write your code inside a for loop that executes the specified number of times (the maximum retry value).

What is retry mechanism in spring boot?

Spring Retry provides an ability to automatically re-invoke a failed operation. This is helpful where the errors may be transient (like a momentary network glitch).

What is a retry mechanism?

This retry mechanism should control the number of retries attempted before giving up and any thread sleep time between attempts. The following is a simplified example of a retry mechanism written in Java to serve as a guide. It employs a helper class, RetryOnException .

What are Retriable exceptions?

retryable() Specifies whether or not an exception can be expected to succeed on a retry.


1 Answers

Off-hand, I can think of two different approaches:

If the differences in exception handling can be expressed declaratively, you might use AOP to weave the exception handling code around your methods. Then, your business code could look like:

@Retry(times = 3, loglevel = LogLevel.INFO)
List<User> getActiveUsers() throws DatabaseException {
    // talk to the database
}

The advantage is that it is really easy to add retry behaviour to a method, the disadvantage is the complexity of weaving the advice (which you only have to implement once. If you are using a dependency injection library, chances are it will offer method interception support).

The other approach is to use the command pattern:

abstract class Retrieable<I,O> {
    private final LogLevel logLevel;

    protected Retrieable(LogLevel loglevel) {
        this.logLevel = loglevel;
    }

    protected abstract O call(I input);

    // subclasses may override to perform custom logic.
    protected void handle(RuntimeException e) {
        // log the exception. 
    }

    public O execute(I input) {
        for (int iteration = 1; ; iteration++) {
            try {
                return call(input);
            } catch (RuntimeException e) {
                if (iteration == helper.getNumberOfRetries()) {
                    throw e;
                } else {
                    handle();
                    utilities.defaultDelayForIteration(iteration);
                }
            }
        }
    }
}

The problem with the command pattern are the method arguments. You are restricted to a single parameter, and the generics are rather unwieldly for the caller. In addition, it won't work with checked exceptions. On the plus side, no fancy AOP stuff :-)

like image 101
meriton Avatar answered Sep 28 '22 10:09

meriton