
com.github.rbuck.retry.RetryPolicy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of java-retry Show documentation
Show all versions of java-retry Show documentation
Lets developers make their applications more resilient by adding robust
transient fault handling logic. Transient faults are errors that occur
because of some temporary condition such as network connectivity issues
or service unavailability. Typically, if you retry the operation that
resulted in a transient error a short time later, you find that the
error has disappeared.
package com.github.rbuck.retry;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Callable;
/**
* A generic retry policy.
*
* @author Robert Buck ([email protected])
*/
public class RetryPolicy {
private final RetryStrategy retryStrategy;
private final TransientExceptionDetector transientExceptionDetector;
/**
* Implements a retry policy using the specified strategy and transient error detection algorithm.
*
* @param retryStrategy the strategy that implements retry
* @param transientExceptionDetector the transient error detection algorithm
*/
public RetryPolicy(RetryStrategy retryStrategy, TransientExceptionDetector transientExceptionDetector) {
this.retryStrategy = retryStrategy;
this.transientExceptionDetector = transientExceptionDetector;
}
/**
* Perform the specified action under the defined retry semantics.
*
* @param callable the action to perform under retry
* @return the result of the action
* @throws Exception inspect cause to determine reason
*/
public V action(Callable callable) throws Exception {
Exception re;
do {
try {
return callable.call();
} catch (Exception e) {
re = e;
if (!transientExceptionDetector.isTransient(e)) {
throw e;
}
}
enqueueRetryEvent(new RetryEvent(this, retryStrategy.getRetryCount(), retryStrategy.getRetryDelay(), re));
delayRetry(retryStrategy.getRetryDelay());
} while (retryStrategy.permitsRetry());
throw re;
}
private void delayRetry(long delay) {
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
Thread.interrupted();
}
}
private RetryEventListener[] retryListeners = new RetryEventListener[0];
/**
* Return this node's preference change listeners. Even though we're using
* a copy-on-write lists, we use synchronized accessors to ensure
* information transmission from the writing thread to the reading thread.
*
* @return the property change listener list
*/
private synchronized RetryEventListener[] retryListeners() {
return retryListeners;
}
public synchronized void addRetryEventListener(RetryEventListener rel) {
if (rel == null) {
throw new IllegalArgumentException("Attempt to set null retry event listener");
}
// Copy-on-write
RetryEventListener[] old = retryListeners;
retryListeners = new RetryEventListener[old.length + 1];
System.arraycopy(old, 0, retryListeners, 0, old.length);
retryListeners[old.length] = rel;
startEventDispatchThreadIfNecessary();
}
private static final List eventQueue = new LinkedList<>();
private static class EventDispatchThread extends Thread {
public void run() {
while (true) {
// Wait on eventQueue till an event is present
RetryEvent event;
synchronized (eventQueue) {
try {
while (eventQueue.isEmpty()) {
eventQueue.wait();
}
event = eventQueue.remove(0);
} catch (InterruptedException e) {
// never eat interrupts!
Thread.currentThread().interrupt();
break;
}
}
// Now we have event & hold no locks; deliver evt to listeners
RetryPolicy src = (RetryPolicy) event.getSource();
RetryEventListener[] listeners = src.retryListeners();
for (RetryEventListener listener : listeners) {
listener.onRetry(event);
}
}
}
}
private static Thread eventDispatchThread = null;
private static synchronized void startEventDispatchThreadIfNecessary() {
if (eventDispatchThread == null) {
eventDispatchThread = new EventDispatchThread();
eventDispatchThread.setDaemon(true);
eventDispatchThread.start();
}
}
private void enqueueRetryEvent(RetryEvent event) {
if (retryListeners.length != 0) {
synchronized (eventQueue) {
eventQueue.add(event);
eventQueue.notify();
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy