From b50390f7cb80facb91f637aaf6a9fcb8d544c61d Mon Sep 17 00:00:00 2001 From: eeichinger Date: Tue, 3 Mar 2009 16:04:19 +0000 Subject: [PATCH] fixed SPRNET-698 --- .../InheritanceBasedAopConfigurer.cs | 3 +- ...AbstractRegularExpressionMethodPointcut.cs | 34 +++-- .../Aop/Support/AttributeMatchingPointcut.cs | 1 + .../Aop/Support/AttributeMethodMatcher.cs | 1 + .../Aop/Support/DynamicMethodMatcher.cs | 3 +- .../Aop/Support/StaticMethodMatcher.cs | 24 ++- .../Spring.Aop/Aop/Support/UnionPointcut.cs | 3 +- .../Aspects/AbstractExceptionHandlerAdvice.cs | 23 ++- .../Exceptions/ExceptionHandlerAdvice.cs | 1 + .../Aspects/Logging/AbstractLoggingAdvice.cs | 69 +++++++-- .../Aspects/Logging/SimpleLoggingAdvice.cs | 3 +- src/Spring/Spring.Aop/Aspects/RetryAdvice.cs | 32 +++- .../Spring.Aop.Tests/AopSerializationTests.cs | 139 ++++++++++++++++++ .../Spring.Aop.Tests.2008.csproj | 1 + 14 files changed, 297 insertions(+), 40 deletions(-) create mode 100644 test/Spring/Spring.Aop.Tests/AopSerializationTests.cs diff --git a/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/InheritanceBasedAopConfigurer.cs b/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/InheritanceBasedAopConfigurer.cs index df4c751d..b4f6fec9 100644 --- a/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/InheritanceBasedAopConfigurer.cs +++ b/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/InheritanceBasedAopConfigurer.cs @@ -330,9 +330,10 @@ namespace Spring.Aop.Framework.AutoProxy #region InheritanceBasedAopTargetSource inner class definition + [Serializable] private class InheritanceBasedAopTargetSource : ITargetSource { - private Type _targetType; + private readonly Type _targetType; public InheritanceBasedAopTargetSource(Type targetType) { diff --git a/src/Spring/Spring.Aop/Aop/Support/AbstractRegularExpressionMethodPointcut.cs b/src/Spring/Spring.Aop/Aop/Support/AbstractRegularExpressionMethodPointcut.cs index b11d4d37..6abb8a1e 100644 --- a/src/Spring/Spring.Aop/Aop/Support/AbstractRegularExpressionMethodPointcut.cs +++ b/src/Spring/Spring.Aop/Aop/Support/AbstractRegularExpressionMethodPointcut.cs @@ -94,17 +94,29 @@ namespace Spring.Aop.Support protected AbstractRegularExpressionMethodPointcut( SerializationInfo info, StreamingContext context) { - _patterns = (object[]) info.GetValue("Patterns", typeof(object[])); - try - { - InitPatternRepresentation(_patterns); - } - catch (Exception ex) - { - throw new AspectException( - "Failed to deserialize AOP regular expression pointcut: " + ex.Message); - } - } + _patterns = (object[]) info.GetValue("Patterns", typeof(object[])); + if (_patterns == null) + { + _patterns = ObjectUtils.EmptyObjects; + } + } + + /// + /// Overridden to ensure proper initialization + /// + protected override void OnDeserialization(object sender) + { + base.OnDeserialization(sender); + try + { + InitPatternRepresentation(_patterns); + } + catch (Exception ex) + { + throw new AspectException( + "Failed to deserialize AOP regular expression pointcut: " + ex.Message); + } + } #endregion diff --git a/src/Spring/Spring.Aop/Aop/Support/AttributeMatchingPointcut.cs b/src/Spring/Spring.Aop/Aop/Support/AttributeMatchingPointcut.cs index 12940a80..df9f51d4 100644 --- a/src/Spring/Spring.Aop/Aop/Support/AttributeMatchingPointcut.cs +++ b/src/Spring/Spring.Aop/Aop/Support/AttributeMatchingPointcut.cs @@ -29,6 +29,7 @@ namespace Spring.Aop.Support /// /// Juergen Hoeller /// Mark Pollack (.NET) + [Serializable] public class AttributeMatchingPointcut : IPointcut { private readonly ITypeFilter typeFilter; diff --git a/src/Spring/Spring.Aop/Aop/Support/AttributeMethodMatcher.cs b/src/Spring/Spring.Aop/Aop/Support/AttributeMethodMatcher.cs index 989ae74c..34cb8965 100644 --- a/src/Spring/Spring.Aop/Aop/Support/AttributeMethodMatcher.cs +++ b/src/Spring/Spring.Aop/Aop/Support/AttributeMethodMatcher.cs @@ -32,6 +32,7 @@ namespace Spring.Aop.Support /// Juergen hoeller /// Mark Pollack /// + [Serializable] public class AttributeMethodMatcher : StaticMethodMatcher { private readonly Type attributeType; diff --git a/src/Spring/Spring.Aop/Aop/Support/DynamicMethodMatcher.cs b/src/Spring/Spring.Aop/Aop/Support/DynamicMethodMatcher.cs index 34ad8422..0d07dbee 100644 --- a/src/Spring/Spring.Aop/Aop/Support/DynamicMethodMatcher.cs +++ b/src/Spring/Spring.Aop/Aop/Support/DynamicMethodMatcher.cs @@ -32,7 +32,8 @@ namespace Spring.Aop.Support /// care about arguments at runtime. /// /// Rod Johnson - /// Aleksandar Seovic (.NET) + /// Aleksandar Seovic (.NET) + [Serializable] public abstract class DynamicMethodMatcher : IMethodMatcher { #region Constructor (s) / Destructor diff --git a/src/Spring/Spring.Aop/Aop/Support/StaticMethodMatcher.cs b/src/Spring/Spring.Aop/Aop/Support/StaticMethodMatcher.cs index 58a16374..81b92042 100644 --- a/src/Spring/Spring.Aop/Aop/Support/StaticMethodMatcher.cs +++ b/src/Spring/Spring.Aop/Aop/Support/StaticMethodMatcher.cs @@ -21,7 +21,8 @@ #region Imports using System; -using System.Reflection; +using System.Reflection; +using System.Runtime.Serialization; #endregion @@ -34,7 +35,7 @@ namespace Spring.Aop.Support /// Rod Johnson /// Aleksandar Seovic (.NET) [Serializable] - public abstract class StaticMethodMatcher : IMethodMatcher + public abstract class StaticMethodMatcher : IMethodMatcher, IDeserializationCallback { /// /// Is this dynamic? @@ -97,6 +98,23 @@ namespace Spring.Aop.Support /// /// if this this method matches statically. /// - public abstract bool Matches(MethodInfo method, Type targetType); + public abstract bool Matches(MethodInfo method, Type targetType); + + + #region Serialization Support + + void IDeserializationCallback.OnDeserialization(object sender) + { + OnDeserialization(sender); + } + + /// + /// Override in case you need to initialized non-serialized fields on deserialization. + /// + protected virtual void OnDeserialization(object sender) + { + } + + #endregion } } \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Support/UnionPointcut.cs b/src/Spring/Spring.Aop/Aop/Support/UnionPointcut.cs index e2e35ee5..8a5f88ff 100644 --- a/src/Spring/Spring.Aop/Aop/Support/UnionPointcut.cs +++ b/src/Spring/Spring.Aop/Aop/Support/UnionPointcut.cs @@ -85,7 +85,8 @@ namespace Spring.Aop.Support /// /// Internal method matcher class for union pointcut. - /// + /// + [Serializable] private sealed class PointcutUnionMethodMatcher : IMethodMatcher { private UnionPointcut _enclosingInstance; diff --git a/src/Spring/Spring.Aop/Aspects/AbstractExceptionHandlerAdvice.cs b/src/Spring/Spring.Aop/Aspects/AbstractExceptionHandlerAdvice.cs index 55f5d21c..9159efb6 100644 --- a/src/Spring/Spring.Aop/Aspects/AbstractExceptionHandlerAdvice.cs +++ b/src/Spring/Spring.Aop/Aspects/AbstractExceptionHandlerAdvice.cs @@ -18,6 +18,8 @@ #endregion +using System; +using System.Runtime.Serialization; using System.Text.RegularExpressions; using AopAlliance.Intercept; using Spring.Objects.Factory; @@ -31,8 +33,9 @@ namespace Spring.Aspects /// /// /// - /// Mark Pollack - public abstract class AbstractExceptionHandlerAdvice : IMethodInterceptor, IInitializingObject + /// Mark Pollack + [Serializable] + public abstract class AbstractExceptionHandlerAdvice : IMethodInterceptor, IInitializingObject, IDeserializationCallback { /// /// Gets or sets the Regex string used to parse advice expressions starting with 'on exception name' and subclass specific actions. @@ -151,6 +154,22 @@ namespace Spring.Aspects RegexOptions options = ((RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline) | RegexOptions.IgnoreCase); Regex reg = new Regex(regexString, options); return reg.Match(adviceExpressionString); + } + + #region Serialization + + void IDeserializationCallback.OnDeserialization(object sender) + { + OnDeserialization(sender); + } + + /// + /// Override in case you need to initialized non-serialized fields on deserialization. + /// + protected virtual void OnDeserialization(object sender) + { } + + #endregion } } \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aspects/Exceptions/ExceptionHandlerAdvice.cs b/src/Spring/Spring.Aop/Aspects/Exceptions/ExceptionHandlerAdvice.cs index 3785a7ee..975a78d4 100644 --- a/src/Spring/Spring.Aop/Aspects/Exceptions/ExceptionHandlerAdvice.cs +++ b/src/Spring/Spring.Aop/Aspects/Exceptions/ExceptionHandlerAdvice.cs @@ -68,6 +68,7 @@ namespace Spring.Aspects.Exceptions /// /// /// Mark Pollack + [Serializable] public class ExceptionHandlerAdvice : AbstractExceptionHandlerAdvice { #region Fields diff --git a/src/Spring/Spring.Aop/Aspects/Logging/AbstractLoggingAdvice.cs b/src/Spring/Spring.Aop/Aspects/Logging/AbstractLoggingAdvice.cs index 24016876..c023034f 100644 --- a/src/Spring/Spring.Aop/Aspects/Logging/AbstractLoggingAdvice.cs +++ b/src/Spring/Spring.Aop/Aspects/Logging/AbstractLoggingAdvice.cs @@ -20,6 +20,7 @@ using System; using System.Reflection; +using System.Runtime.Serialization; using AopAlliance.Intercept; using Common.Logging; using Spring.Aop.Framework; @@ -32,25 +33,44 @@ namespace Spring.Aspects.Logging /// /// /// - /// Mark Pollack - public abstract class AbstractLoggingAdvice : IMethodInterceptor + /// Mark Pollack + [Serializable] + public abstract class AbstractLoggingAdvice : IMethodInterceptor, IDeserializationCallback { #region Fields /// /// The default ILog instance used to write logging messages. - /// - protected ILog defaultLogger = LogManager.GetLogger(MethodInfo.GetCurrentMethod().DeclaringType); + /// + [NonSerialized] + protected ILog defaultLogger; + + /// + /// The name of the logger instance to use for obtaining from . + /// + private string defaultLoggerName; /// /// Indicates whether or not proxy type names should be hidden when using dynamic loggers. /// private bool hideProxyTypeNames = false; - #endregion - - #region Properties - + #endregion + + #region Constructor + + /// + /// Creates a new advice instance using this advice type's name for logging by default. + /// + protected AbstractLoggingAdvice() + { + SetDefaultLogger(MethodBase.GetCurrentMethod().DeclaringType.FullName); + } + + #endregion + + #region Properties + /// /// Sets a value indicating whether to use a dynamic logger or static logger /// @@ -68,8 +88,8 @@ namespace Spring.Aspects.Logging public bool UseDynamicLogger { set - { - defaultLogger = (value ? null : LogManager.GetLogger(GetType())); + { + SetDefaultLogger(value ? null : this.GetType().FullName); } } @@ -91,8 +111,8 @@ namespace Spring.Aspects.Logging public string LoggerName { set - { - defaultLogger = LogManager.GetLogger(value); + { + SetDefaultLogger(value); } } @@ -234,6 +254,29 @@ namespace Spring.Aspects.Logging } } - #endregion + #endregion + + /// + /// Sets the default logger to the given name. + /// + /// if null, the default logger is removed. + protected void SetDefaultLogger(string name) + { + defaultLogger = (name == null ? null : LogManager.GetLogger(name)); + defaultLoggerName = name; + } + + void IDeserializationCallback.OnDeserialization(object sender) + { + OnDeserialization(sender); + } + + /// + /// Override in case you need to initialized non-serialized fields on deserialization. + /// + protected virtual void OnDeserialization(object sender) + { + SetDefaultLogger(this.defaultLoggerName); + } } } \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aspects/Logging/SimpleLoggingAdvice.cs b/src/Spring/Spring.Aop/Aspects/Logging/SimpleLoggingAdvice.cs index 2135e1ce..22a0ddf2 100644 --- a/src/Spring/Spring.Aop/Aspects/Logging/SimpleLoggingAdvice.cs +++ b/src/Spring/Spring.Aop/Aspects/Logging/SimpleLoggingAdvice.cs @@ -32,7 +32,8 @@ namespace Spring.Aspects.Logging /// /// /// - /// Mark Pollack + /// Mark Pollack + [Serializable] public class SimpleLoggingAdvice : AbstractLoggingAdvice { #region Fields diff --git a/src/Spring/Spring.Aop/Aspects/RetryAdvice.cs b/src/Spring/Spring.Aop/Aspects/RetryAdvice.cs index b2ab7fd8..3d9b186a 100644 --- a/src/Spring/Spring.Aop/Aspects/RetryAdvice.cs +++ b/src/Spring/Spring.Aop/Aspects/RetryAdvice.cs @@ -37,15 +37,22 @@ namespace Spring.Aspects /// /// /// - /// Mark Pollack + /// Mark Pollack + [Serializable] public class RetryAdvice : AbstractExceptionHandlerAdvice - { + { + private static readonly ILog log; + private static readonly TimeSpanConverter timeSpanConverter; + + static RetryAdvice() + { + log = LogManager.GetLogger(typeof(RetryAdvice)); + timeSpanConverter = new TimeSpanConverter(); + } + #region Fields - private static readonly ILog log = LogManager.GetLogger(typeof (RetryAdvice)); - - private TimeSpanConverter timeSpanConverter = new TimeSpanConverter(); - + [NonSerialized] private RetryExceptionHandler retryExceptionHandler; private string retryExpression; @@ -309,7 +316,18 @@ namespace Spring.Aspects RegexOptions options = ((RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline) | RegexOptions.IgnoreCase); Regex reg = new Regex(regexString, options); return reg.Match(actionExpressionString); + } + + /// + /// Override in case you need to initialized non-serialized fields on deserialization. + /// + protected override void OnDeserialization(object sender) + { + base.OnDeserialization(sender); + if (retryExpression != null) + { + this.AfterPropertiesSet(); + } } - } } \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/AopSerializationTests.cs b/test/Spring/Spring.Aop.Tests/AopSerializationTests.cs new file mode 100644 index 00000000..f4ae778b --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/AopSerializationTests.cs @@ -0,0 +1,139 @@ +#region License + +/* + * Copyright 2002-2009 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; +using System.IO; +using System.Reflection; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters; +using System.Runtime.Serialization.Formatters.Binary; +using AopAlliance.Aop; +using NUnit.Framework; +using Spring.Aop; +using Spring.Aop.Framework; +using Spring.Aop.Support; +using Spring.Context; +using Spring.Objects.Factory; + +namespace Spring +{ + /// + /// Ensure, that all framework implementations of IAdvice and IAdvisor are serializable + /// + /// Erich Eichinger + [TestFixture] + public class AopSerializationTests + { + public AopSerializationTests() + { } + + [Test] + public void AllAopInfrastructureTypesAreSerializable() + { + ArrayList brokenTypes = new ArrayList(); + + foreach (Type t in GetTypesToTest()) + { + if (!ExcludeTypeFromTest(t) + && IsAopInfrastructureType(t)) + { + if (!CheckIsSerializable(t)) + { + brokenTypes.Add(string.Format("{0} or one of its base classes are not marked as serializable\n", t.FullName)); + continue; + } + if (t.IsAbstract) continue; + + // perform a fast ser/deser check + try + { + object o = FormatterServices.GetSafeUninitializedObject(t); + o = SerializeAndDeserialize(o); + } + catch (SerializationException sex) + { + brokenTypes.Add(string.Format("{0}: {1}\n", t.FullName, sex.Message)); + } + catch (Exception ex) + { + Console.WriteLine(string.Format("WARN: {0}: {1}\n", t.FullName, ex)); + } + } + } + + Assert.IsEmpty(brokenTypes); + } + + protected bool CheckIsSerializable(Type t) + { + if (t == typeof(object)) return true; + + return (t.IsSerializable && CheckIsSerializable(t.BaseType)); + } + + protected virtual ICollection GetTypesToTest() + { + return GetAssemblyToTest().GetTypes(); + // return new Type[] { typeof(DynamicMethodMatcherPointcutAdvisor) }; + } + + protected virtual Assembly GetAssemblyToTest() + { + return typeof(IAdvice).Assembly; + } + + protected virtual bool ExcludeTypeFromTest(Type t) + { + return false //t.IsAbstract + || t.IsInterface + || typeof(IApplicationContextAware).IsAssignableFrom(t) + || typeof(IObjectFactoryAware).IsAssignableFrom(t) + ; + } + + protected virtual bool IsAopInfrastructureType(Type t) + { + return typeof(IAdvisedSupportListener).IsAssignableFrom(t) + || typeof(ITargetSource).IsAssignableFrom(t) + || typeof(IAdvice).IsAssignableFrom(t) + || typeof(IAdvisor).IsAssignableFrom(t) + || typeof(IMethodMatcher).IsAssignableFrom(t) + || typeof(IPointcut).IsAssignableFrom(t); + } + + private object SerializeAndDeserialize(object s) + { + // Serialize the session + using (Stream stream = new MemoryStream()) + { + BinaryFormatter formatter = new BinaryFormatter(); + formatter.AssemblyFormat = FormatterAssemblyStyle.Full; + formatter.TypeFormat = FormatterTypeStyle.TypesAlways; + formatter.Serialize(stream, s); + + // Deserialize the session + stream.Position = 0; + object res = formatter.Deserialize(stream); + return res; + } + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Spring.Aop.Tests.2008.csproj b/test/Spring/Spring.Aop.Tests/Spring.Aop.Tests.2008.csproj index 1a03e27a..3dce2ff2 100644 --- a/test/Spring/Spring.Aop.Tests/Spring.Aop.Tests.2008.csproj +++ b/test/Spring/Spring.Aop.Tests/Spring.Aop.Tests.2008.csproj @@ -106,6 +106,7 @@ Code +