All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.rbuck.retry.RetryPolicy Maven / Gradle / Ivy

Go to download

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.

There is a newer version: 1.2
Show newest version
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