28

I have a number of situations where I need to retry a task n-times if it fails (sometimes with some form of back-off-before-retry logic). Generally, if an exception is thrown, the task should be retried up to the max-retry count.

I can easily write something to do this fairly generically, but not wanting to re-invent the wheel I was wondering if anyone can recommend any frameworks for this. The only thing I have been able to find is: Ant Retry but I don't want to use Ant tasks directly in my application.

Thanks

Scruffers
  • 4,984
  • 8
  • 35
  • 41
  • what kind of task you want retry? – fmucar Jan 19 '11 at 17:33
  • 8
    @fatih - It doesn't matter, retrying a task is an abstract concept. – Scruffers Jan 21 '11 at 16:50
  • Interestingly, everyone seems to wrap the action to retry in some class that does the retry, which requires the action to be retried to conform to some interface. I would rather implement a backoff/retry strategy manager with just one method `boolean shallWeRetryOnceMore()`, when called it either immediately returns `false` or waits for a time, depending on backoff algorithm, and then returns true. – Harald Mar 11 '16 at 16:46

8 Answers8

18

Check out Failsafe (which I authored). It's a simple, zero-dependency library for performing retries, and supports synchronous and asynchronous retries, Java 8 integration, event listeners, integration with other async APIs, etc:

RetryPolicy retryPolicy = new RetryPolicy()
  .handle(ConnectException.class, SocketException.class);
  .withMaxRetries(3);

Connection connection = Failsafe.with(retryPolicy).get(() -> connect());

Doesn't get much easier.

Jonathan
  • 5,027
  • 39
  • 48
  • 1
    Can I use FailSafe to keep retrying until a "condition" is met ? I want to keep making an api call until certain conditions are met or until a few tries. Examples of conditions - response code should be correct, OR response code should be correct and response should contain certain things etc. – MasterJoe Apr 05 '19 at 23:41
16

You can use RetriableTasks as outlined in this post: Retrying Operations in Java. You can quite easily change its waiting algorithm if you like.

Sample code:

//creates a task which will retry 3 times with an interval of 5 seconds
RetriableTask r = new RetriableTask(3, 5000, new Callable(){
    public Object call() throws Exception{
        //put your code here
    }
});
r.call();
dogbane
  • 266,786
  • 75
  • 396
  • 414
9

If you use Spring:

//import the necessary classes
import org.springframework.batch.retry.RetryCallback;
import org.springframework.batch.retry.RetryContext;
import org.springframework.batch.retry.backoff.ExponentialBackOffPolicy;
import org.springframework.batch.retry.policy.SimpleRetryPolicy;
import org.springframework.batch.retry.support.RetryTemplate;
...

// create the retry template
final RetryTemplate template = new RetryTemplate();
template.setRetryPolicy(new SimpleRetryPolicy(5));
final ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
backOffPolicy.setInitialInterval(1000L);
template.setBackOffPolicy(backOffPolicy);

// execute the operation using the retry template
template.execute(new RetryCallback<Remote>() {
  @Override
  public Remote doWithRetry(final RetryContext context) throws Exception {
    return (Remote) Naming.lookup("rmi://somehost:2106/MyApp");
  }
});

Original blog post

mulya
  • 1,273
  • 13
  • 26
7

If you are using Spring, it is very simple using Spring Retry Library.

Now, Spring Retry is an individual library (earlier it was part of Spring Batch) framework.

Step1: Add spring retry dependency.

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
    <version>1.1.2.RELEASE</version>
</dependency>

Step2: Add @EnableRetry annotation to your class which contains main() method of your application or into any of your @Configuration class .

Step3: Add @Retryable annotation to your method which you want to retry/call again, in case of exceptions.

@Retryable(maxAttempts=5,backoff = @Backoff(delay = 3000))
public void retrySomething() throws Exception{
    logger.info("printSomething{} is called");
    throw new SQLException();
}

This @Retryable annotation will retry/call retrySomething() 5 times (including the 1st failure).

Current thread will wait for 3000 ms or 3 seconds between next retry.

Sundararaj Govindasamy
  • 8,180
  • 5
  • 44
  • 77
3

I have one answer already, but it was three years ago and I have to add that now I absolutley love guava-retrying project. Let me just show you the code.

Callable<Boolean> callable = new Callable<Boolean>() {
    public Boolean call() throws Exception {
        return true; // do something useful here
    }
};

Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
        .retryIfResult(Predicates.<Boolean>isNull())
        .retryIfExceptionOfType(IOException.class)
        .retryIfRuntimeException()
        .withStopStrategy(StopStrategies.stopAfterAttempt(3))
        .build();
try {
    retryer.call(callable);
} catch (RetryException e) {
    e.printStackTrace();
} catch (ExecutionException e) {
    e.printStackTrace();
}
BillMan
  • 9,434
  • 9
  • 32
  • 52
mulya
  • 1,273
  • 13
  • 26
  • 4
    Of course Guava is a dependency: https://github.com/rholder/guava-retrying/blob/master/build.gradle#L84 Otherwise your code example wouldn't compile. – Jonathan Mar 31 '16 at 05:16
  • This is neat and StopStrategies abstraction is cool. Tho, I am unclear whether this works well (or at all) for implementing different retry strategies like a regular cadence retry at a fixed time interval, or exponential back-off, or staggered. – g7p Nov 15 '16 at 21:13
  • @mulya - The last commit was in may 2016. Either no one is maintaining that project anymore or the project does not need any maintenance at all. So, that is a concern for me. Btw, what do you think about the FailSafe library ? – MasterJoe Apr 05 '19 at 18:17
  • 1
    @MasterJoe2 No maintenance is sad, you right. If you need something bulletproof you can try hystrix or resilience4j. Never hear of FailSafe actually – mulya Apr 09 '19 at 10:00
1

One option to factor this out of your codebase is to use the command pattern between the components of your application. Once you turn a call to a business method into an object, you can hand around the call easily and have an abstract RetryHandler that takes a command and retries it. This should be independent from the actual call and reusable.

Jochen Bedersdorfer
  • 4,093
  • 24
  • 26
  • Thanks Jochen, this is broadly what I have since the tasks are either Runnable or Callable instances anyway. I was just hoping their might be a framework that provided options such as specifying different types of retry logic and back-off algorithms... – Scruffers Jan 19 '11 at 17:34
  • Not that I'm aware of, no. Which is quite unusual, I'd agree. We use retries mostly to deal with optimistic exceptions when talking to the persistence layer. – Jochen Bedersdorfer Jan 19 '11 at 17:40
0

I have implemented a pretty flexible retry utility here

You can retry any Callable with:

public static <T> T executeWithRetry(final Callable<T> what, final int nrImmediateRetries,
        final int nrTotalRetries, final int retryWaitMillis, final int timeoutMillis,
        final Predicate<? super T> retryOnReturnVal, final Predicate<Exception> retryOnException)

with immediate + delayed retries, with a max timeout, and retry either on decisions based on result or exception.

There are several other versions of this function with more or less flexibility.

I have written also a aspect that can be applied with annotations Retry Retry Aspect

Henrik Aasted Sørensen
  • 6,966
  • 11
  • 51
  • 60
user2179737
  • 493
  • 3
  • 6
-1

You can use Quartz. Look at this Stack Overflow answer.

Community
  • 1
  • 1
Amir Afghani
  • 37,814
  • 16
  • 84
  • 124
  • This is not a useful general purpose solution for retrying. It is specifically about retrying failed quartz jobs. – deamon Apr 26 '19 at 14:02