In my last post I walked through an implementation of a class that retries an operation N times before failing. However after reading this post I realized that using Java’s Callable class cleans things up quite a bit. This completely removes the need for a Retryable interface, and leaves you with Retrier implemented as
package org.mccv; import java.util.concurrent.Callable; /** * The implementation of our retrying class */ public class Retryer<T> { /** * The retryable we will call */ private Callable<T> retryable; /** * Just assign our retryable field */ public Retryer(Callable<T> retryable){ this.retryable = retryable; } /** * Try to execute our retryable n times */ public T tryTimes(int times) throws Exception{ // store the last thrown recoverable exception RecoverableException lastException = null; // try the specified number of times for(int i = 0; i < times; i++){ System.out.println("running it with " + (times-i) + " tries remaining"); try{ return retryable.call(); }catch(RecoverableException re){ lastException = re; } } throw lastException; } }
And an implementation/usage demonstration as
package org.mccv; import java.util.concurrent.Callable; /** * An implementation of the Retryable interface */ public class RetryableImpl implements Callable<String>{ /** * Show usage of the retryer */ public static void main(String [] args){ // create a new retrier Retryer<String> retrier = new Retryer<String>(new RetryableImpl()); // run the retrier try{ System.out.println("result = " + retrier.tryTimes(3)); }catch(Exception e){ System.out.println("failed after 3 tries"); } } /** * An intentionally flaky operation */ public String call() throws Exception{ if(Math.random() > 0.3){ throw new RecoverableException(); }else{ return "foo"; } } }
The Scala version is still cleaner, but this Java version is a substantial improvement over the first version. As Andrew said in the biotext post, getting to know (or in my case remembering) the more advanced Java concurrency classes is a good thing.