Global variable support for retry clients. Normally it is not necessary for clients to be aware of the surrounding environment because a IRetryCallback{T} can always use the context it is passed by the enclosing IRetryOperations{T}. But occasionally it might be helpful to have lower level access to the ongoing IRetryContext so we provide a global accessor here. The mutator methods ({@link #clear()} and
Esempio n. 1
0
 private IRetryContext DoOpenInternal(IRetryPolicy retryPolicy)
 {
     return(retryPolicy.Open(RetrySynchronizationManager.GetContext()));
 }
Esempio n. 2
0
        protected T DoExecute <T>(object retryCallback, object recoveryCallback, IRetryState state)
        {
            if (retryCallback == null || (!(retryCallback is IRetryCallback <T>) & !(retryCallback is Func <IRetryContext, T>)))
            {
                throw new ArgumentException("retryCallback must be an IRetryCallback<T> or a Func<IRetryContext, T>");
            }

            if (recoveryCallback != null && (!(recoveryCallback is IRetryCallback <T>) & !(recoveryCallback is Func <IRetryContext, T>)))
            {
                throw new ArgumentException("recoveryCallback must be an IRecoveryCallback<T> or a Func<IRetryContext, T>");
            }

            var retryPolicy   = this.retryPolicy;
            var backOffPolicy = this.backOffPolicy;

            // Allow the retry policy to initialise itself...
            var context = this.Open(retryPolicy, state);

            Logger.Trace(m => m("RetryContext retrieved: {0}", context));

            // Make sure the context is available globally for clients who need
            // it...
            RetrySynchronizationManager.Register(context);

            Exception lastException = null;

            try
            {
                // Give clients a chance to enhance the context...
                var running = retryCallback is IRetryCallback <T>?this.DoOpenInterceptors((IRetryCallback <T>) retryCallback, context) : this.DoOpenInterceptors((Func <IRetryContext, T>)retryCallback, context);

                if (!running)
                {
                    throw new TerminatedRetryException("Retry terminated abnormally by interceptor before first attempt");
                }

                // Get or Start the backoff context...
                IBackOffContext    backOffContext    = null;
                IAttributeAccessor attributeAccessor = null;
                if (context is IAttributeAccessor)
                {
                    attributeAccessor = context;
                    var resource = attributeAccessor.GetAttribute("backOffContext");
                    if (resource is IBackOffContext)
                    {
                        backOffContext = (IBackOffContext)resource;
                    }
                }

                if (backOffContext == null)
                {
                    backOffContext = backOffPolicy.Start(context);
                    if (attributeAccessor != null && backOffContext != null)
                    {
                        attributeAccessor.SetAttribute("backOffContext", backOffContext);
                    }
                }

                // 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 (this.CanRetry(retryPolicy, context) && !context.ExhaustedOnly)
                {
                    try
                    {
                        Logger.Debug(m => m("Retry: count={0}", context.RetryCount));

                        // Reset the last exception, so if we are successful
                        // the close interceptors will not think we failed...
                        lastException = null;
                        return(retryCallback is IRetryCallback <T>?((IRetryCallback <T>)retryCallback).DoWithRetry(context) : ((Func <IRetryContext, T>)retryCallback).Invoke(context));
                    }
                    catch (Exception e)
                    {
                        lastException = e;

                        if (retryCallback is IRetryCallback <T> )
                        {
                            this.DoOnErrorInterceptors((IRetryCallback <T>)retryCallback, context, e);
                        }
                        else
                        {
                            this.DoOnErrorInterceptors((Func <IRetryContext, T>)retryCallback, context, e);
                        }

                        try
                        {
                            this.RegisterThrowable(retryPolicy, state, context, e);
                        }
                        catch (Exception ex)
                        {
                            throw new TerminatedRetryException("Could not register exception", ex);
                        }

                        if (this.CanRetry(retryPolicy, context) && !context.ExhaustedOnly)
                        {
                            try
                            {
                                backOffPolicy.BackOff(backOffContext);
                            }
                            catch (BackOffInterruptedException ex)
                            {
                                lastException = e;

                                // back off was prevented by another thread - fail
                                // the retry
                                Logger.Debug(m => m("Abort retry because interrupted: count={0}", context.RetryCount));
                                throw;
                            }
                        }

                        Logger.Debug(m => m("Checking for rethrow: count={0}", context.RetryCount));
                        if (this.ShouldRethrow(retryPolicy, context, state))
                        {
                            Logger.Debug(m => m("Rethrow in retry for policy: count={0}", context.RetryCount));
                            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.
                }

                Logger.Debug(m => m("Retry failed last attempt: count={0}" + context.RetryCount));

                if (context.ExhaustedOnly)
                {
                    throw new ExhaustedRetryException("Retry exhausted after last attempt with no recovery path.", context.LastException);
                }

                if (recoveryCallback == null)
                {
                    return(this.HandleRetryExhausted(default(IRecoveryCallback <T>), context, state));
                }
                else if (recoveryCallback is IRecoveryCallback <T> )
                {
                    return(this.HandleRetryExhausted((IRecoveryCallback <T>)recoveryCallback, context, state));
                }
                else
                {
                    return(this.HandleRetryExhausted((Func <IRetryContext, T>)recoveryCallback, context, state));
                }
            }
            finally
            {
                this.Close(retryPolicy, context, state, lastException == null);
                if (retryCallback is IRetryCallback <T> )
                {
                    this.DoCloseInterceptors((IRetryCallback <T>)retryCallback, context, lastException);
                }
                else
                {
                    this.DoCloseInterceptors((Func <IRetryContext, T>)retryCallback, context, lastException);
                }

                RetrySynchronizationManager.Clear();
            }
        }