diff --git a/src/Spring/Spring.Aop/Aop/Support/AttributeMatchMethodPointcutAdvisor.cs b/src/Spring/Spring.Aop/Aop/Support/AttributeMatchMethodPointcutAdvisor.cs index d72f960c..4fb30f5d 100644 --- a/src/Spring/Spring.Aop/Aop/Support/AttributeMatchMethodPointcutAdvisor.cs +++ b/src/Spring/Spring.Aop/Aop/Support/AttributeMatchMethodPointcutAdvisor.cs @@ -53,12 +53,31 @@ namespace Spring.Aop.Support /// class /// for the supplied . /// - /// + /// the advice to apply if the pointcut matches public AttributeMatchMethodPointcutAdvisor(IAdvice advice) { this._advice = advice; } + /// + /// Creates a new instance of the + /// class + /// for the supplied . + /// + /// + /// The to match. + /// + /// + /// Flag that controls whether or not the inheritance tree of the + /// method to be included in the search for the ? + /// + /// the advice to apply if the pointcut matches + public AttributeMatchMethodPointcutAdvisor(Type attribute, bool inherit, IAdvice advice) + :base(attribute, inherit) + { + this._advice = advice; + } + /// /// Is this advice associated with a particular instance? /// diff --git a/src/Spring/Spring.Aop/Aspects/RetryAdvice.cs b/src/Spring/Spring.Aop/Aspects/RetryAdvice.cs index 3d9b186a..a364cc86 100644 --- a/src/Spring/Spring.Aop/Aspects/RetryAdvice.cs +++ b/src/Spring/Spring.Aop/Aspects/RetryAdvice.cs @@ -41,6 +41,11 @@ namespace Spring.Aspects [Serializable] public class RetryAdvice : AbstractExceptionHandlerAdvice { + /// + ///The type of the callback that is called for delaying retries. + /// + public delegate void SleepHandler(TimeSpan duration); + private static readonly ILog log; private static readonly TimeSpanConverter timeSpanConverter; @@ -52,6 +57,8 @@ namespace Spring.Aspects #region Fields + private SleepHandler sleepHandler; + [NonSerialized] private RetryExceptionHandler retryExceptionHandler; @@ -102,6 +109,21 @@ namespace Spring.Aspects #endregion + /// + /// Creates a new RetryAdvice instance, using for delaying retries + /// + public RetryAdvice() :this(new SleepHandler(Thread.Sleep)) + { + } + + /// + /// Creates a new RetryAdvice instance, using any arbitrary callback for delaying retries + /// + public RetryAdvice(SleepHandler sleepHandler) + { + this.sleepHandler = sleepHandler; + } + #region IMethodInterceptor implementation /// @@ -158,7 +180,7 @@ namespace Spring.Aspects log.Trace("Retrying " + invocation.Method.Name); } callContextDictionary["n"] = numAttempts; - Sleep(retryExceptionHandler, callContextDictionary); + Sleep(retryExceptionHandler, callContextDictionary, sleepHandler); } } else @@ -173,11 +195,11 @@ namespace Spring.Aspects return returnVal; } - private static void Sleep(RetryExceptionHandler handler, IDictionary callContextDictionary) + private static void Sleep(RetryExceptionHandler handler, IDictionary callContextDictionary, SleepHandler sleepHandler) { if (handler.IsDelayBased) { - Thread.Sleep(handler.DelayTimeSpan); + sleepHandler(handler.DelayTimeSpan); } else { @@ -186,19 +208,19 @@ namespace Spring.Aspects IExpression expression = Expression.Parse(handler.DelayRateExpression); object result = expression.GetValue(null, callContextDictionary); decimal d = decimal.Parse(result.ToString()); - decimal rounded = decimal.Round(d*1000,0); - int sleepInSeconds = decimal.ToInt32(rounded); - Thread.Sleep(sleepInSeconds); + decimal rounded = decimal.Round(d*1000,0); + TimeSpan duration = TimeSpan.FromMilliseconds(decimal.ToDouble(rounded)); + sleepHandler(duration); } catch (InvalidCastException e) { - log.Warn("Was not able to cast expression to decimal [" + handler.DelayRateExpression + "]. Sleeping for 1 second", e); - Thread.Sleep(1000); + log.Warn("Was not able to cast expression to decimal [" + handler.DelayRateExpression + "]. Sleeping for 1 second", e); + sleepHandler(new TimeSpan(0,0,1)); } catch (Exception e) { - log.Warn("Was not able to evaluate rate expression [" + handler.DelayRateExpression + "]. Sleeping for 1 second", e); - Thread.Sleep(1000); + log.Warn("Was not able to evaluate rate expression [" + handler.DelayRateExpression + "]. Sleeping for 1 second", e); + sleepHandler(new TimeSpan(0,0,1)); } } } diff --git a/test/Spring/Spring.Aop.Tests/Aspects/RetryAdviceTests.cs b/test/Spring/Spring.Aop.Tests/Aspects/RetryAdviceTests.cs index 05ace27c..f109c69a 100644 --- a/test/Spring/Spring.Aop.Tests/Aspects/RetryAdviceTests.cs +++ b/test/Spring/Spring.Aop.Tests/Aspects/RetryAdviceTests.cs @@ -21,6 +21,7 @@ #region Imports using System; +using System.Collections; using NUnit.Framework; using Spring.Aop.Framework; @@ -58,23 +59,28 @@ namespace Spring.Aspects private static void InvokeOncePassOnceFail(bool useExceptionName, bool isDelay) { - ITestRemoteService rs = GetRemoteService(2, useExceptionName, isDelay); + TestSleepHandler testSleepHandler = new TestSleepHandler(); + ITestRemoteService rs = GetRemoteService(2, useExceptionName, isDelay, testSleepHandler); rs.DoTransfer(); + AssertSleepsAndReset(testSleepHandler, 2, isDelay); - rs = GetRemoteService(3, useExceptionName, isDelay); + rs = GetRemoteService(3, useExceptionName, isDelay, testSleepHandler); try { rs.DoTransfer(); Assert.Fail("Should have failed."); } catch (ArithmeticException) { - + // they maximum retry count is 3, thus only 2 sleep calls + AssertSleepsAndReset(testSleepHandler, 2, isDelay); } } + private static void InvokeOnceFailWithUnexceptedException(bool useExceptionName, bool isDelay) { - ITestRemoteService rs = GetRemoteService(3, useExceptionName, isDelay); + TestSleepHandler testSleepHandler = new TestSleepHandler(); + ITestRemoteService rs = GetRemoteService(3, useExceptionName, isDelay, testSleepHandler); try { rs.DoTransfer2(); @@ -86,12 +92,40 @@ namespace Spring.Aspects } } - private static ITestRemoteService GetRemoteService(int numFailures, bool usingExceptionName, bool isDelay) + private static void AssertSleepsAndReset(TestSleepHandler testSleepHandler, int numFailures, bool isDelay) + { + Assert.AreEqual(numFailures, testSleepHandler.CalledTimeSpans.Count); + for(int i=0;iFalse ..\..\..\lib\Net\2.0\Common.Logging.dll - - False - ..\..\..\lib\Net\2.0\nunit.core.interfaces.dll - False ..\..\..\lib\Net\2.0\nunit.framework.dll