/*******************************************************************************
* Copyright (c) 2014 Pivotal Software, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal Software, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.util;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.Callable;
import java.util.function.Predicate;
import org.springsource.ide.eclipse.commons.livexp.util.ExceptionUtil;
/**
* Helper class to implement simple retry logic that allows retrying a task
* a set number of times at an interval until it succeeds or until the timelimit
* is exceeded.
*
* @author Kris De Volder
*/
public class RetryUtil {
public static void retryWhen(String name, int tries, Predicate<Throwable> when, Thunk task) throws Exception {
boolean success = false;
Throwable error = null;
while (!success && tries>0) {
tries--;
try {
task.call();
success = true;
} catch (Throwable e) {
error = e;
if (name!=null) {
if (tries>0 && when.test(e)) {
System.out.println(name+" failed: "+ExceptionUtil.getMessage(e));
System.out.println("Retrying!");
}
}
}
}
if (!success) {
throw ExceptionUtil.exception(error);
}
}
public static void retryTimes(String name, int tries, Thunk task) throws Exception {
retryWhen(name, tries, (e) -> true, task);
}
/**
* Call a given callable. If it throws then we retry it after a given interval.
* We keep retrying it periodically until either the call completes successfully
* or the timelimit is exceeded.
* <p>
* If the time limit is exceeded without reaching a succesful call, the last thrown
* exception is rethrown.
*/
public static <T> T retry(long interval, long timelimit, Callable<T> task) throws Exception {
T result = null;
boolean success = false;
Throwable error = null;
long endTime = System.currentTimeMillis() + timelimit;
do {
try {
result = task.call();
success = true;
} catch (Throwable e) {
error = e;
try {
Thread.sleep(interval);
} catch (InterruptedException ignore) {
}
}
} while (!success && System.currentTimeMillis() < endTime);
if (success) {
return result;
} else {
if (error instanceof Exception) {
throw (Exception)error;
} else {
throw new InvocationTargetException(error);
}
}
}
public static Predicate<Throwable> errorWithMsg(String msgFrag) {
return (error) -> {
String msg = ExceptionUtil.getMessage(error);
return msg.contains(msgFrag);
};
}
/**
* Periodically retry calling a given 'body' until a condition holds on the returned value or until timeout.
* The last value will be returned.
* <p>
* This is just like a 'repeat until' loop. It does not handle exceptions. If an exception occurs during
* any of the condition or body execution the loop is immediately aborted.
*/
public static <T> T until(long interval, long timeout, Predicate<T> condition, Callable<T> body) throws Exception {
long endTime = Math.max(System.currentTimeMillis()+timeout, timeout); //Math.max to guard against overflow (if timeout is Long.MAX_VALUE for example).
T result = body.call();
while (System.currentTimeMillis() < endTime && !condition.test(result)) {
Thread.sleep(interval);
result = body.call();
}
return result;
}
}