Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * Copyright 2006-2007 the original author or authors.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 package org.springframework.batch.retry.support;
 
 import java.util.List;
 
Template class that simplifies the execution of operations with retry semantics.
Retryable operations are encapsulated in implementations of the org.springframework.batch.retry.RetryCallback interface and are executed using one of the supplied execute methods.
By default, an operation is retried if is throws any java.lang.Exception or subclass of java.lang.Exception. This behaviour can be changed by using the setRetryPolicy(org.springframework.batch.retry.RetryPolicy) method.
Also by default, each operation is retried for a maximum of three attempts with no back off in between. This behaviour can be configured using the setRetryPolicy(org.springframework.batch.retry.RetryPolicy) and setBackOffPolicy(org.springframework.batch.retry.backoff.BackOffPolicy) properties. The org.springframework.batch.retry.backoff.BackOffPolicy controls how long the pause is between each individual retry attempt.
This class is thread-safe and suitable for concurrent access when executing operations and when performing configuration changes. As such, it is possible to change the number of retries on the fly, as well as the org.springframework.batch.retry.backoff.BackOffPolicy used and no in progress retryable operations will be affected.

Author(s):
Rob Harrop
Dave Syer
 
 public class RetryTemplate implements RetryOperations {
 
 	protected final Log logger = LogFactory.getLog(getClass());
 
 	private volatile BackOffPolicy backOffPolicy = new NoBackOffPolicy();
 
 	private volatile RetryPolicy retryPolicy = new SimpleRetryPolicy(3, Collections
 			.<Class<? extends Throwable>, BooleansingletonMap(Exception.classtrue));
 
 	private volatile RetryListener[] listeners = new RetryListener[0];
 
 
 	public void setRetryContextCache(RetryContextCache retryContextCache) {
 		this. = retryContextCache;
 	}

Setter for listeners. The listeners are executed before and after a retry block (i.e. before and after all the attempts), and on an error (every attempt).

Parameters:
listeners
See also:
org.springframework.batch.retry.RetryListener
	public void setListeners(RetryListener[] listeners) {
		this. = Arrays.asList(listeners).toArray(new RetryListener[listeners.length]);
	}

Register an additional listener.

	public void registerListener(RetryListener listener) {
		list.add(listener);
		 = list.toArray(new RetryListener[list.size()]);
	}

Setter for org.springframework.batch.retry.backoff.BackOffPolicy.

Parameters:
backOffPolicy
	public void setBackOffPolicy(BackOffPolicy backOffPolicy) {
		this. = backOffPolicy;
	}

Setter for org.springframework.batch.retry.RetryPolicy.

Parameters:
retryPolicy
	public void setRetryPolicy(RetryPolicy retryPolicy) {
		this. = retryPolicy;
	}

Keep executing the callback until it either succeeds or the policy dictates that we stop, in which case the most recent exception thrown by the callback will be rethrown.

	public final <T> T execute(RetryCallback<T> retryCallbackthrows Exception {
		return doExecute(retryCallbacknullnull);
	}

Keep executing the callback until it either succeeds or the policy dictates that we stop, in which case the recovery callback will be executed.

	public final <T> T execute(RetryCallback<T> retryCallbackRecoveryCallback<T> recoveryCallbackthrows Exception {
		return doExecute(retryCallbackrecoveryCallbacknull);
	}

Execute the callback once if the policy dictates that we can, re-throwing any exception encountered so that clients can re-present the same task later.

	public final <T> T execute(RetryCallback<T> retryCallbackRetryState retryStatethrows Exception,
		return doExecute(retryCallbacknullretryState);
	}

Execute the callback once if the policy dictates that we can, re-throwing any exception encountered so that clients can re-present the same task later.

	public final <T> T execute(RetryCallback<T> retryCallbackRecoveryCallback<T> recoveryCallback,
			RetryState retryStatethrows ExceptionExhaustedRetryException {
		return doExecute(retryCallbackrecoveryCallbackretryState);
	}

	protected <T> T doExecute(RetryCallback<T> retryCallbackRecoveryCallback<T> recoveryCallbackRetryState state)
		RetryPolicy retryPolicy = this.;
		BackOffPolicy backOffPolicy = this.;
		// Allow the retry policy to initialise itself...
		RetryContext context = open(retryPolicystate);
		.debug("RetryContext retrieved: " + context);
		// Make sure the context is available globally for clients who need
		// it...
		RetrySynchronizationManager.register(context);
		Throwable lastException = null;
		try {
			// Give clients a chance to enhance the context...
			boolean running = doOpenInterceptors(retryCallbackcontext);
			if (!running) {
				throw new TerminatedRetryException("Retry terminated abnormally by interceptor before first attempt");
			}
			// Start the backoff context...
			BackOffContext backOffContext = backOffPolicy.start(context);
			/*
			 * We allow the whole loop to be skipped if the policy or context
			 * already forbid the first try. This is used in the case of
			 * external retry to allow a recovery in handleRetryExhausted
			 * without the callback processing (which would throw an exception).
			 */
			while (canRetry(retryPolicycontext) && !context.isExhaustedOnly()) {
				try {
					.debug("Retry: count=" + context.getRetryCount());
					// Reset the last exception, so if we are successful
					// the close interceptors will not think we failed...
					lastException = null;
					return retryCallback.doWithRetry(context);
				}
				catch (Throwable e) {
					lastException = e;
					doOnErrorInterceptors(retryCallbackcontexte);
					registerThrowable(retryPolicystatecontexte);
					if (canRetry(retryPolicycontext) && !context.isExhaustedOnly()) {
						try {
							backOffPolicy.backOff(backOffContext);
						}
							lastException = e;
							// back off was prevented by another thread - fail
							// the retry
							.debug("Abort retry because interrupted: count=" + context.getRetryCount());
							throw ex;
						}
					}
					.debug("Checking for rethrow: count=" + context.getRetryCount());
					if (shouldRethrow(retryPolicycontextstate)) {
						.debug("Rethrow in retry for policy: count=" + context.getRetryCount());
						throw wrapIfNecessary(e);
					}
				}
				/*
				 * A stateful attempt that can retry should have rethrown the
				 * exception by now - i.e. we shouldn't get this far for a
				 * stateful attempt if it can retry.
				 */
			}
			.debug("Retry failed last attempt: count=" + context.getRetryCount());
			if (context.isExhaustedOnly()) {
				throw new ExhaustedRetryException("Retry exhausted after last attempt with no recovery path."context
			}
			return handleRetryExhausted(recoveryCallbackcontextstate);
		}
		finally {
			close(retryPolicycontextstatelastException == null);
			doCloseInterceptors(retryCallbackcontextlastException);
			RetrySynchronizationManager.clear();
		}
	}

Decide whether to proceed with the ongoing retry attempt. This method is called before the org.springframework.batch.retry.RetryCallback is executed, but after the backoff and open interceptors.

Parameters:
retryPolicy the policy to apply
context the current retry context
Returns:
true if we can continue with the attempt
	protected boolean canRetry(RetryPolicy retryPolicyRetryContext context) {
		return retryPolicy.canRetry(context);
	}

Clean up the cache if necessary and close the context provided (if the flag indicates that processing was successful).

Parameters:
context
state
succeeded
	protected void close(RetryPolicy retryPolicyRetryContext contextRetryState stateboolean succeeded) {
		if (state != null) {
			if (succeeded) {
				retryPolicy.close(context);
			}
		}
		else {
			retryPolicy.close(context);
		}
	}

Parameters:
retryPolicy
state
context
e
	protected void registerThrowable(RetryPolicy retryPolicyRetryState stateRetryContext contextThrowable e) {
		if (state != null) {
			Object key = state.getKey();
			if (context.getRetryCount() > 0 && !.containsKey(key)) {
				throw new RetryException("Inconsistent state for failed item key: cache key has changed. "
"Consider whether equals() or hashCode() for the key might be inconsistent, "
"or if you need to supply a better key");
			}
			.put(keycontext);
		}
		retryPolicy.registerThrowable(contexte);
	}

Delegate to the org.springframework.batch.retry.RetryPolicy having checked in the cache for an existing value if the state is not null.

Parameters:
retryPolicy a org.springframework.batch.retry.RetryPolicy to delegate the context creation
Returns:
a retry context, either a new one or the one used last time the same state was encountered
	protected RetryContext open(RetryPolicy retryPolicyRetryState state) {
		if (state == null) {
			return doOpenInternal(retryPolicy);
		}
		Object key = state.getKey();
		if (state.isForceRefresh()) {
			return doOpenInternal(retryPolicy);
		}
		// If there is no cache hit we can avoid the possible expense of the
		// cache re-hydration.
			// The cache is only used if there is a failure.
			return doOpenInternal(retryPolicy);
		}
		if (context == null) {
				throw new RetryException("Inconsistent state for failed item: no history found. "
"Consider whether equals() or hashCode() for the item might be inconsistent, "
"or if you need to supply a better ItemKeyGenerator");
			}
			// The cache could have been expired in between calls to
			// containsKey(), so we have to live with this:
			return doOpenInternal(retryPolicy);
		}
		return context;
	}

Parameters:
retryPolicy
Returns:
	private RetryContext doOpenInternal(RetryPolicy retryPolicy) {
		return retryPolicy.open(RetrySynchronizationManager.getContext());
	}

Actions to take after final attempt has failed. If there is state clean up the cache. If there is a recovery callback, execute that and return its result. Otherwise throw an exception.

Parameters:
recoveryCallback the callback for recovery (might be null)
context the current retry context
Throws:
java.lang.Exception if the callback does, and if there is no callback and the state is null then the last exception from the context
org.springframework.batch.retry.ExhaustedRetryException if the state is not null and there is no recovery callback
	protected <T> T handleRetryExhausted(RecoveryCallback<T> recoveryCallbackRetryContext contextRetryState state)
			throws Exception {
		if (state != null) {
		}
		if (recoveryCallback != null) {
			return recoveryCallback.recover(context);
		}
		if (state != null) {
			.debug("Retry exhausted after last attempt with no recovery path.");
			throw new ExhaustedRetryException("Retry exhausted after last attempt with no recovery path"context
		}
	}

Extension point for subclasses to decide on behaviour after catching an exception in a org.springframework.batch.retry.RetryCallback. Normal stateless behaviour is not to rethrow, and if there is state we rethrow.

Parameters:
retryPolicy
context the current context
Returns:
true if the state is not null but subclasses might choose otherwise
	protected boolean shouldRethrow(RetryPolicy retryPolicyRetryContext contextRetryState state) {
		if (state == null) {
			return false;
		}
		else {
			return state.rollbackFor(context.getLastThrowable());
		}
	}
	private <T> boolean doOpenInterceptors(RetryCallback<T> callbackRetryContext context) {
		boolean result = true;
		for (int i = 0; i < .i++) {
			result = result && [i].open(contextcallback);
		}
		return result;
	}
	private <T> void doCloseInterceptors(RetryCallback<T> callbackRetryContext contextThrowable lastException) {
		for (int i = .i-- > 0;) {
			[i].close(contextcallbacklastException);
		}
	}
	private <T> void doOnErrorInterceptors(RetryCallback<T> callbackRetryContext contextThrowable throwable) {
		for (int i = .i-- > 0;) {
			[i].onError(contextcallbackthrowable);
		}
	}

Re-throws the original throwable if it is unchecked, wraps checked exceptions into org.springframework.batch.repeat.RepeatException.
	private static Exception wrapIfNecessary(Throwable throwable) {
		if (throwable instanceof Error) {
			throw (Errorthrowable;
		}
		else if (throwable instanceof Exception) {
			return (Exceptionthrowable;
		}
		else {
			return new RetryException("Exception in batch process"throwable);
		}
	}
New to GrepCode? Check out our FAQ X