Update of /cvsroot/springnet/Spring.Net/src/Spring/Spring.Aop/Aspects
In directory sc8-pr-cvs8.sourceforge.net:/tmp/cvs-serv6806/Aspects
Modified Files:
RetryAdvice.cs
Added Files:
RetryExceptionHandler.cs
Log Message:
Add retry advice
Index: RetryAdvice.cs
===================================================================
RCS file: /cvsroot/springnet/Spring.Net/src/Spring/Spring.Aop/Aspects/RetryAdvice.cs,v
retrieving revision 1.1
retrieving revision 1.2
diff -C2 -d -r1.1 -r1.2
*** RetryAdvice.cs 8 Oct 2007 22:05:16 -0000 1.1
--- RetryAdvice.cs 10 Oct 2007 07:38:17 -0000 1.2
***************
*** 22,33 ****
using System.Collections;
using System.Text.RegularExpressions;
using AopAlliance.Intercept;
using Common.Logging;
! using Spring.Objects.Factory;
namespace Spring.Aspects
{
/// <summary>
! /// AOP Advice to retry a method invocation on an exception
/// </summary>
/// <remarks>
--- 22,37 ----
using System.Collections;
using System.Text.RegularExpressions;
+ using System.Threading;
using AopAlliance.Intercept;
using Common.Logging;
! using Spring.Core.TypeConversion;
! using Spring.Expressions;
namespace Spring.Aspects
{
/// <summary>
! /// AOP Advice to retry a method invocation on an exception. The retry semantics are defined by a DSL of the
! /// form <code>on exception name [ExceptionName1,ExceptionName2,...] retry [number of times] [delay|rate] [delay time|rate expression]</code>.
! /// For example, <code>on exception name ArithmeticException retry 3x delay 1s</code>
/// </summary>
/// <remarks>
***************
*** 36,40 ****
/// <author>Mark Pollack</author>
/// <version>$Id$</version>
! public class RetryAdvice : IMethodInterceptor, IInitializingObject
{
#region Fields
--- 40,44 ----
/// <author>Mark Pollack</author>
/// <version>$Id$</version>
! public class RetryAdvice : AbstractExceptionHandlerAdvice
{
#region Fields
***************
*** 42,51 ****
private static readonly ILog log = LogManager.GetLogger(typeof (RetryAdvice));
! private IExceptionHandler exceptionHandler;
! private string retryExpression;
! private int maxRetries;
#endregion
--- 46,64 ----
private static readonly ILog log = LogManager.GetLogger(typeof (RetryAdvice));
! private TimeSpanConverter timeSpanConverter = new TimeSpanConverter();
! private RetryExceptionHandler retryExceptionHandler;
! private string retryExpression;
!
! private string onExceptionNameRegex = @"^(on\s+exception\s+name)\s+(.*?)\s+(retry)\s*(.*?)$";
!
! private string onExceptionRegex = @"^(on\s+exception\s+)(\(.*?\))\s+(retry)\s*(.*?)$";
!
! //retry 3x delay 10s
! private string delayRegex = @"^(\d+)x\s+(delay)\s+(\d+\w+)?$";
+ //retry 3x rate 10n+5
+ private string rateRegex = @"^(\d+)x\s+(rate)\s+(\(.*?\))?$";
#endregion
***************
*** 53,74 ****
/// <summary>
! /// Gets or sets the max retries.
/// </summary>
! /// <value>The max retries.</value>
! public int MaxRetries
{
! get { return maxRetries; }
! set { maxRetries = value; }
}
/// <summary>
! /// Gets or sets the retry expression.
/// </summary>
! /// <value>The retry expression.</value>
! public string RetryExpression
{
! get { return retryExpression; }
! set { retryExpression = value; }
}
--- 66,96 ----
/// <summary>
! /// Gets or sets the retry expression.
/// </summary>
! /// <value>The retry expression.</value>
! public string RetryExpression
{
! get { return retryExpression; }
! set { retryExpression = value; }
}
+ /// <summary>
+ /// Gets or sets the Regex string used to parse advice expressions starting with 'on exception name' and exception handling actions.
+ /// </summary>
+ /// <value>The regex string to parse advice expressions starting with 'on exception name' and exception handling actions.</value>
+ public override string OnExceptionNameRegex
+ {
+ get { return onExceptionNameRegex; }
+ set { onExceptionNameRegex = value; }
+ }
/// <summary>
! /// Gets or sets the Regex string used to parse advice expressions starting with 'on exception (constraint)' and exception handling actions.
/// </summary>
! /// <value>The regex string to parse advice expressions starting with 'on exception (constraint)' and exception handling actions.</value>
! public override string OnExceptionRegex
{
! get { return onExceptionRegex; }
! set { onExceptionRegex = value; }
}
***************
*** 98,102 ****
/// throws an exception.
/// </exception>
! public object Invoke(IMethodInvocation invocation)
{
IDictionary callContextDictionary = new Hashtable();
--- 120,124 ----
/// throws an exception.
/// </exception>
! public override object Invoke(IMethodInvocation invocation)
{
IDictionary callContextDictionary = new Hashtable();
***************
*** 134,142 ****
catch (Exception ex)
{
! callContextDictionary.Add("e", ex);
! if (exceptionHandler.CanHandleException(ex, callContextDictionary))
{
numAttempts++;
! if (numAttempts == MaxRetries)
{
throw;
--- 156,164 ----
catch (Exception ex)
{
! callContextDictionary["e"] = ex;
! if (retryExceptionHandler.CanHandleException(ex, callContextDictionary))
{
numAttempts++;
! if (numAttempts == retryExceptionHandler.MaximumRetryCount)
{
throw;
***************
*** 144,158 ****
else
{
! //Sleep()
}
}
}
! } while (numAttempts <= MaxRetries);
! log.Info("Invoked successfully after " + numAttempts + " attempt(s)");
return returnVal;
}
#endregion
--- 166,211 ----
else
{
! callContextDictionary["n"] = numAttempts;
! Sleep(retryExceptionHandler, callContextDictionary);
}
}
}
! } while (numAttempts <= retryExceptionHandler.MaximumRetryCount);
! log.Debug("Invoked successfully after " + numAttempts + " attempt(s)");
return returnVal;
}
+ private void Sleep(RetryExceptionHandler handler, IDictionary callContextDictionary)
+ {
+ if (handler.IsDelayBased)
+ {
+ Thread.Sleep(handler.DelayTimeSpan);
+ }
+ else
+ {
+ try
+ {
+ IExpression expression = Expression.Parse(handler.DelayRateExpression);
+ object result = expression.GetValue(null, callContextDictionary);
+ decimal d = decimal.Parse(result.ToString());
+ decimal rounded = decimal.Round(d*1000);
+ int sleepInSeconds = decimal.ToInt32(rounded);
+ Thread.Sleep(sleepInSeconds);
+ }
+ catch (InvalidCastException e)
+ {
+ log.Warn("Was not able to cast expression to decimal [" + handler.DelayRateExpression + "]. Sleeping for 1 second", e);
+ Thread.Sleep(1000);
+ }
+ catch (Exception e)
+ {
+ log.Warn("Was not able to evaluate rate expression [" + handler.DelayRateExpression + "]. Sleeping for 1 second", e);
+ Thread.Sleep(1000);
+ }
+ }
+ }
+
#endregion
***************
*** 185,200 ****
/// required property) or if initialization fails.
/// </exception>
! public void AfterPropertiesSet()
{
! if (exceptionHandler == null)
{
throw new ArgumentException("Must specify retry expression.");
}
! IExceptionHandler handler = Parse(retryExpression);
if (handler == null)
{
! throw new ArgumentException("Was not able to parse exception handler string [" + retryExpression + "]");
}
! exceptionHandler = handler;
}
--- 238,253 ----
/// required property) or if initialization fails.
/// </exception>
! public override void AfterPropertiesSet()
{
! if (retryExpression == null)
{
throw new ArgumentException("Must specify retry expression.");
}
! RetryExceptionHandler handler = Parse(retryExpression);
if (handler == null)
{
! throw new ArgumentException("Was not able to parse retry expression string [" + retryExpression + "]");
}
! retryExceptionHandler = handler;
}
***************
*** 204,215 ****
/// Parses the specified handler string.
/// </summary>
! /// <param name="handlerString">The handler string.</param>
/// <returns></returns>
! protected virtual IExceptionHandler Parse(string handlerString)
{
! return null;
}
}
}
\ No newline at end of file
--- 257,326 ----
/// Parses the specified handler string.
/// </summary>
! /// <param name="retryExpressionString">The handler string.</param>
/// <returns></returns>
! protected virtual RetryExceptionHandler Parse(string retryExpressionString)
{
! ParsedAdviceExpression parsedAdviceExpression = ParseAdviceExpression(retryExpressionString);
!
! if (!parsedAdviceExpression.Success)
! {
! log.Warn("Could not parse retry expression " + retryExpressionString);
! return null;
! }
!
! RetryExceptionHandler handler = new RetryExceptionHandler(parsedAdviceExpression.ExceptionNames);
! handler.ConstraintExpressionText = parsedAdviceExpression.ConstraintExpression;
! handler.ActionExpressionText = parsedAdviceExpression.AdviceExpression;
!
! Match match = GetMatchForActionExpression(parsedAdviceExpression.ActionExpressionText, delayRegex);
!
! if (match.Success)
! {
! handler.MaximumRetryCount = int.Parse(match.Groups[1].Value.Trim());
! handler.IsDelayBased = true;
!
! try
! {
! string ts = match.Groups[3].Value.Trim();
! handler.DelayTimeSpan = (TimeSpan) timeSpanConverter.ConvertFrom(null, null, ts);
! } catch (Exception)
! {
! log.Warn("Could not parse timespan " + match.Groups[3].Value.Trim());
! return null;
! }
! return handler;
! }
! else
! {
! match = GetMatchForActionExpression(parsedAdviceExpression.ActionExpressionText, rateRegex);
! if (match.Success)
! {
! handler.MaximumRetryCount = int.Parse(match.Groups[1].Value.Trim());
! handler.IsDelayBased = false;
! handler.DelayRateExpression = match.Groups[3].Value.Trim();
! return handler;
! }
! else
! {
! return null;
! }
! }
}
+
+ /// <summary>
+ /// Gets the match for action expression.
+ /// </summary>
+ /// <param name="actionExpressionString">The action expression string.</param>
+ /// <param name="regexString">The regex string.</param>
+ /// <returns>The Match object resulting from the regular expression match.</returns>
+ protected virtual Match GetMatchForActionExpression(string actionExpressionString, string regexString)
+ {
+ RegexOptions options = ((RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline) | RegexOptions.IgnoreCase);
+ Regex reg = new Regex(regexString, options);
+ return reg.Match(actionExpressionString);
+ }
+
}
}
\ No newline at end of file
--- NEW FILE: RetryExceptionHandler.cs ---
#region License
/*
* Copyright 2002-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.
*/
#endregion
using System;
using System.Collections;
namespace Spring.Aspects
{
/// <summary>
/// Sleeps for the appropriate amount of time for an exception.
/// </summary>
/// <remarks>
///
/// </remarks>
/// <author>Mark Pollack</author>
/// <version>$Id: RetryExceptionHandler.cs,v 1.1 2007/10/10 07:38:17 markpollack Exp $</version>
public class RetryExceptionHandler : AbstractExceptionHandler
{
#region Fields
private int maximumRetryCount;
private bool isDelayBased;
private TimeSpan delayTimeSpan;
private string delayRateExpression;
#endregion
#region Constructor
/// <summary>
/// Initializes a new instance of the <see cref="RetryExceptionHandler"/> class.
/// </summary>
public RetryExceptionHandler()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="RetryExceptionHandler"/> class.
/// </summary>
/// <param name="exceptionNames">The exception names.</param>
public RetryExceptionHandler(string[] exceptionNames) : base(exceptionNames)
{
}
#endregion
#region Properties
/// <summary>
/// Gets the maximum retry count.
/// </summary>
/// <value>The maximum retry count.</value>
public int MaximumRetryCount
{
get { return maximumRetryCount; }
set { maximumRetryCount = value; }
}
/// <summary>
/// Gets a value indicating whether this instance is delay based.
/// </summary>
/// <value>
/// <c>true</c> if this instance is delay based; otherwise, <c>false</c>.
/// </value>
public bool IsDelayBased
{
get { return isDelayBased; }
set { isDelayBased = value; }
}
/// <summary>
/// Gets or sets the delay time span to sleep after an exception is thrown and a rety is
/// attempted.
/// </summary>
/// <value>The delay time span.</value>
public TimeSpan DelayTimeSpan
{
get { return delayTimeSpan; }
set { delayTimeSpan = value; }
}
/// <summary>
/// Gets or sets the delay rate expression.
/// </summary>
/// <value>The delay rate expression.</value>
public string DelayRateExpression
{
get { return delayRateExpression; }
set { delayRateExpression = value; }
}
#endregion
#region Methods
/// <summary>
/// Handles the exception.
/// </summary>
/// <param name="callContextDictionary"></param>
/// <returns>
/// The return value from handling the exception, if not rethrown or a new exception is thrown.
/// </returns>
public override object HandleException(IDictionary callContextDictionary)
{
return null;
}
#endregion
}
}
|