6
votes

In my program I call a method that can either return a value or "", and basically I want it to retry looking up the value 3 times with a delay of 1 second inbetween each try until it gets a value or the tries are finished.

This is what I currently have but I feel like this solution is pretty gross, particularly having to put the try/catch around the sleep. Does anyone know of a better way to retry with a delay?

public void method(String input){
    String value = getValue(input);

    int tries = 0
    while (value.equals("") && tries < 3){
        try { 
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            log.error("Thread interrupted");
        }
        value = getValue(input);
        tries += 1;
    }

    return value;
}
5
Looks pretty straightforward, except for the fact that you're swallowing the InterruptedException. There's a library for these kind of tasks called retrier, but I can't say I've used it. - shmosel

5 Answers

3
votes

At the end of the day you can not get around the fact that in needs to catch the Exception

What you can do though is hide it by putting in in your own method

public static void mySleep (int val) {
    try { 
        TimeUnit.SECONDS.sleep(val);
    } catch (InterruptedException e) {
        log.error("Thread interrupted");
    }
}

So you main function becomes cleaner as

while (value.equals("") && tries < 3){
    mySleep (1);
    value = getValue(input);
    tries += 1;
}
5
votes

Fixed Poll Interval feature of awaitility might help:

Awaitility.with()
  .pollInterval(1, SECONDS)
  .atMost(3, SECONDS)
  .await()
  .until(() -> ("" != getValue(input)));

It offers a fluent interface for synchronizing asynchronous operations.

1
votes

I do not see anything wrong with using try .. catch.

This is in harmony with the basic philosophy that an exception is not an error - it is just a situation that happens, a way of handling the control flow.

So - I like your solution. I would not change that.

0
votes

Your example works, and basically any solution is going to have that general form at its center. If you don't like the verbosity of writing your own retry, check out Failsafe and Guava-Retrying, which are libraries to accomplish the same basic thing.

0
votes

Here's an example with Thread.sleep and catching interrupted exception. The main issue is it will block the thread until it finishes. To avoid this you'd need some application server level support to allow it undertow for example

    private void sendWithRetry(final InternetAddress[] toAddresses, final Transport transport, final MimeMessage msg) throws MessagingException {

        boolean guard = false;
        for (int retry = 0; !guard && retry < SMTP_RETRY_LIMIT - 1; ++retry) {
            try (final Transport trans = transport) {
                trans.connect();

                // Actually send the message
                trans.sendMessage(msg, toAddresses);

                // Close the connection
                guard = true;
            } catch (final MessagingException me) {
                log.warn("Retry {}", retry, me);
                try {
                    Thread.sleep(SMTP_RETRY_DELAY_IN_MS);
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    log.warn("Interrupt exception received so interrupting thread and breaking out of retry loop");
                    guard = true;
                }
            }
        }
        if (!guard) {
            try (final Transport trans = transport) {
                trans.connect();

                // Actually send the message
                trans.sendMessage(msg, toAddresses);
            }
        }
    }