SPRNET-883 - In Process - Add support for translation of exceptions using PersistenceExceptionTranslationInterceptor

This commit is contained in:
markpollack
2008-10-09 01:37:16 +00:00
parent d10a266a0e
commit 72722094e1
18 changed files with 1346 additions and 1 deletions

View File

@@ -143,7 +143,7 @@ namespace Spring.Aop.Support
}
/// <summary>
/// Is the interfaces attributes of the method to be included in the search for the
/// Is the interfaces attributes of the method to be included in the search for theg
/// <see cref="Attribute"/>?
/// </summary>
/// <remarks>

View File

@@ -0,0 +1,121 @@
#region License
/*
* Copyright 2002-2008 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 Spring.Util;
namespace Spring.Aop.Support
{
/// <summary>
/// Pointcut that looks for a specific attribute being present on a class or
/// method.
/// </summary>
/// <author>Juergen Hoeller</author>
/// <author>Mark Pollack (.NET)</author>
public class AttributeMatchingPointcut : IPointcut
{
private readonly ITypeFilter typeFilter;
private readonly IMethodMatcher methodMatcher;
/// <summary>
/// Initializes a new instance of the <see cref="AttributeMatchingPointcut"/> class for the
/// given attribute type.
/// </summary>
/// <param name="attributeType">Type of the attribute to look for at the class level.</param>
public AttributeMatchingPointcut(Type attributeType)
{
ValidateAttributeTypeArgument(attributeType);
this.typeFilter = new AttributeTypeFilter(attributeType);
this.methodMatcher = TrueMethodMatcher.True;
}
/// <summary>
/// Initializes a new instance of the <see cref="AttributeMatchingPointcut"/> class for the
/// given attribute type
/// </summary>
/// <param name="attributeType">Type of the attribute.</param>
/// <param name="checkInherited">if set to <c>true</c> [check inherited].</param>
public AttributeMatchingPointcut(Type attributeType, bool checkInherited)
{
ValidateAttributeTypeArgument(attributeType);
this.typeFilter = new AttributeTypeFilter(attributeType, checkInherited);
this.methodMatcher = TrueMethodMatcher.True;
}
/// <summary>
/// Initializes a new instance of the <see cref="AttributeMatchingPointcut"/> class for the given
/// attribute type
/// </summary>
/// <param name="classAttributeType">The attribute type to look for at the class level.</param>
/// <param name="methodAttributeType">The attribute type to look for at the method attribute.</param>
public AttributeMatchingPointcut(Type classAttributeType, Type methodAttributeType)
{
AssertUtils.IsTrue(classAttributeType != null || methodAttributeType != null,
"Either Type attribute type or Method attribute type needs to be specified (or both)");
if (classAttributeType != null)
{
this.typeFilter = new AttributeTypeFilter(classAttributeType);
} else
{
this.typeFilter = TrueTypeFilter.True;
}
if (methodAttributeType != null)
{
this.methodMatcher = new AttributeMethodMatcher(methodAttributeType);
} else
{
this.methodMatcher = TrueMethodMatcher.True;
}
}
/// <summary>
/// The <see cref="Spring.Aop.ITypeFilter"/> for this pointcut.
/// </summary>
/// <value>The current <see cref="Spring.Aop.ITypeFilter"/>.</value>
public ITypeFilter TypeFilter
{
get { return this.typeFilter; }
}
/// <summary>
/// The <see cref="Spring.Aop.IMethodMatcher"/> for this pointcut.
/// </summary>
/// <value>The current <see cref="Spring.Aop.IMethodMatcher"/>.</value>
public IMethodMatcher MethodMatcher
{
get { return this.methodMatcher; }
}
private static void ValidateAttributeTypeArgument(Type attributeType)
{
AssertUtils.ArgumentNotNull(attributeType, "attributeType");
if (!typeof(Attribute).IsAssignableFrom(attributeType))
{
throw new ArgumentException(
string.Format(
"The [{0}] Type must be derived from the [System.Attribute] class.",
attributeType));
}
}
}
}

View File

@@ -0,0 +1,109 @@
#region License
/*
* Copyright 2002-2008 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.Reflection;
using Spring.Util;
namespace Spring.Aop.Support
{
/// <summary>
/// MethodMatcher that looks for a specific attribute being present on the
/// method (checking both the method on the onviked interface, if any and the corresponding
/// method on the target class
/// </summary>
/// <author>Juergen hoeller</author>
/// <author>Mark Pollack</author>
/// <seealso cref="AttributeMatchingPointcut"/>
public class AttributeMethodMatcher : StaticMethodMatcher
{
private readonly Type attributeType;
/// <summary>
/// Initializes a new instance of the <see cref="AttributeMethodMatcher"/> class for the
/// given atribute type.
/// </summary>
/// <param name="attributeType">Type of the attribute to look for.</param>
public AttributeMethodMatcher(Type attributeType)
{
ValidateAttributeTypeArgument(attributeType);
this.attributeType = attributeType;
}
/// <summary>
/// Does the supplied <paramref name="method"/> satisfy this matcher?
/// </summary>
/// <param name="method">The candidate method.</param>
/// <param name="targetType">The target <see cref="System.Type"/> (may be <see langword="null"/>,
/// in which case the candidate <see cref="System.Type"/> must be taken
/// to be the <paramref name="method"/>'s declaring class).</param>
/// <returns>
/// <see langword="true"/> if this this method matches statically.
/// </returns>
/// <remarks>
/// <p>
/// Must be implemented by a derived class in order to specify matching
/// rules.
/// </p>
/// </remarks>
public override bool Matches(MethodInfo method, Type targetType)
{
if (method.IsDefined(attributeType, true))
{
// Checks whether the attribute is defined on the method or a super definition of the method
// but does not check attributes on implemented interfaces.
return true;
}
else
{
Type[] parameterTypes = ReflectionUtils.GetParameterTypes(method);
// Also check whether the attribute is defined on a method implemented from an interface.
// First find all interfaces for the type that contains the method.
// Next, check each interface for the presence of the attribute on the corresponding
// method from the interface.
foreach (Type interfaceType in method.DeclaringType.GetInterfaces())
{
MethodInfo intfMethod = interfaceType.GetMethod(method.Name, parameterTypes);
if (intfMethod != null && intfMethod.IsDefined(attributeType, true))
{
return true;
}
}
return false;
}
}
private static void ValidateAttributeTypeArgument(Type attributeType)
{
AssertUtils.ArgumentNotNull(attributeType, "attributeType");
if (!typeof(Attribute).IsAssignableFrom(attributeType))
{
throw new ArgumentException(
string.Format(
"The [{0}] Type must be derived from the [System.Attribute] class.",
attributeType));
}
}
}
}

View File

@@ -0,0 +1,111 @@
#region License
/*
* Copyright 2002-2008 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 Spring.Util;
namespace Spring.Aop.Support
{
/// <summary>
/// ITypeFilter that looks for a specific attribute being present on a class
/// </summary>
/// <author>Juergen Hoeller</author>
/// <author>Mark Pollack (.NET)</author>
public class AttributeTypeFilter : ITypeFilter
{
private readonly Type attributeType;
private readonly bool checkInherited;
/// <summary>
/// Initializes a new instance of the <see cref="AttributeTypeFilter"/> class for the
/// given attribute type.
/// </summary>
/// <param name="attributeType">Type of the attribute to look for.</param>
public AttributeTypeFilter(Type attributeType) : this(attributeType, false)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="AttributeTypeFilter"/> class for the
/// given attribute type.
/// </summary>
/// <param name="attributeType">Type of the attribute.</param>
/// <param name="checkInherited">if set to <c>true</c> [check inherited].</param>
public AttributeTypeFilter(Type attributeType, bool checkInherited)
{
#region parameter validation
AssertUtils.ArgumentNotNull(attributeType, "attributeType");
if (!typeof(Attribute).IsAssignableFrom(attributeType))
{
throw new ArgumentException(
string.Format(
"The [{0}] Type must be derived from the [System.Attribute] class.",
attributeType));
}
#endregion
this.attributeType = attributeType;
this.checkInherited = checkInherited;
}
/// <summary>
/// Should the pointcut apply to the supplied <see cref="System.Type"/>?
/// </summary>
/// <param name="type">The candidate <see cref="System.Type"/>.</param>
/// <returns>
/// <see langword="true"/> if the advice should apply to the supplied
/// <paramref name="type"/>
/// </returns>
public bool Matches(Type type)
{
if (checkInherited)
{
return FindAttribute(type, attributeType) != null;
} else
{
return Attribute.GetCustomAttributes(type, attributeType, false) != null;
}
}
private Attribute FindAttribute(Type type, Type attribType)
{
Attribute[] attributes = Attribute.GetCustomAttributes(type, attributeType, false); // we will traverse hierarchy ourselves.
if (attributes.Length > 0)
{
return attributes[0];
}
foreach (Type interfaceType in type.GetInterfaces())
{
Attribute attrib = FindAttribute(interfaceType, attribType);
if (attrib != null)
{
return attrib;
}
}
if (type.BaseType == null)
{
return null;
}
return FindAttribute(type.BaseType, attribType);
}
}
}

View File

@@ -281,12 +281,15 @@
<Compile Include="Aop\Support\AbstractRegularExpressionMethodPointcut.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Aop\Support\AttributeMatchingPointcut.cs" />
<Compile Include="Aop\Support\AttributeMatchMethodPointcut.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Aop\Support\AttributeMatchMethodPointcutAdvisor.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Aop\Support\AttributeMethodMatcher.cs" />
<Compile Include="Aop\Support\AttributeTypeFilter.cs" />
<Compile Include="Aop\Support\ComposablePointcut.cs">
<SubType>Code</SubType>
</Compile>

View File

@@ -956,6 +956,9 @@
<Compile Include="Reflection\Dynamic\DynamicMethod.cs" />
<Compile Include="Reflection\Dynamic\DynamicProperty.cs" />
<Compile Include="Reflection\Dynamic\DynamicReflectionManager.cs" />
<Compile Include="Stereotype\ComponentAttribute.cs" />
<Compile Include="Stereotype\RepositoryAttribute.cs" />
<Compile Include="Stereotype\ServiceAttribute.cs" />
<Compile Include="Threading\CallContextStorage.cs" />
<Compile Include="Threading\ISync.cs" />
<Compile Include="Threading\IThreadStorage.cs" />

View File

@@ -0,0 +1,54 @@
using System;
namespace Spring.Stereotype {
/// <summary>
/// Indicates that an annotated class is a "component".
/// Such classes are considered as candidates for future features such
/// as auto-detection when using attribute-based configuration and assembly scanning.
/// </summary>
/// <remarks>Other class-level annotations may be considered as identifying
/// a component as well, typically a special kind of component:
/// e.g. the Repository attribute.
/// </remarks>
/// <author>Mark Fisher</author>
/// <author>Mark Pollack (.NET)</author>
/// <seealso cref="RepositoryAttribute"/>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, Inherited = true)]
[Serializable]
public class ComponentAttribute : Attribute
{
private string name = "";
/// <summary>
/// Initializes a new instance of the <see cref="ComponentAttribute"/> class.
/// </summary>
public ComponentAttribute()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ComponentAttribute"/> class.
/// </summary>
/// <param name="name">The name of the component.</param>
public ComponentAttribute(string name)
{
this.name = name;
}
/// <summary>
/// Gets or sets the name of the component
/// </summary>
/// <value>The name of the component.</value>
public string Name
{
get { return name; }
set { name = value; }
}
}
}

View File

@@ -0,0 +1,60 @@
#region License
/*
* Copyright 2002-2008 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;
namespace Spring.Stereotype
{
/// <summary>
/// Indicates that an annotated class is a "Repository" (or "DAO").
/// </summary>
/// <remarks>
/// A class with this attribute is eligible for Spring DataAccessException translation. A class
/// with the Repository attribute is also clarified as to its role in the overall application
/// architecture for the purpose of tools, aspects, etc.
/// <para>
/// This attribute also serves as a specialization of the ComponentAttribute, allowing implementation
/// classes to be autodetected in future releases through assembly scanning.
/// </para>
/// </remarks>
/// <author>Rod Johnson</author>
/// <author>Jueren Hoeller</author>
/// <author>Mark Pollack (.NET)</author>
/// <seealso cref="ComponentAttribute"/>
public class RepositoryAttribute : ComponentAttribute
{
/// <summary>
/// Initializes a new instance of the <see cref="RepositoryAttribute"/> class.
/// </summary>
public RepositoryAttribute()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="RepositoryAttribute"/> class.
/// </summary>
/// <param name="name">The name of the repository.</param>
public RepositoryAttribute(string name) : base(name)
{
}
}
}

View File

@@ -0,0 +1,54 @@
#region License
/*
* Copyright 2002-2008 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
namespace Spring.Stereotype
{
/// <summary>
/// Indicates that an annotated class is a "Service" (e.g. a business service facade).
/// </summary>
/// <remarks>
/// <para>
/// This attribute also serves as a specialization of the ComponentAttribute, allowing implementation
/// classes to be autodetected in future releases through assembly scanning.
/// </para>
/// </remarks>
/// <author>Juergen Hoeller</author>
/// <author>Mark Pollack (.NET)</author>
public class ServiceAttribute : ComponentAttribute
{
/// <summary>
/// Initializes a new instance of the <see cref="ServiceAttribute"/> class.
/// </summary>
public ServiceAttribute()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ServiceAttribute"/> class.
/// </summary>
/// <param name="name">The name.</param>
public ServiceAttribute(string name) : base(name)
{
}
}
}

View File

@@ -0,0 +1,96 @@
#region License
/*
* Copyright 2002-2008 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 AopAlliance.Aop;
using Spring.Aop;
using Spring.Aop.Support;
using Spring.Dao.Support;
using Spring.Objects.Factory;
namespace Spring.Dao.Attributes
{
/// <summary>
/// Spring AOP exception translation aspect for use at Repository or DAO layer level.
/// Translates native persistence exceptions into Spring's DataAccessException hierarchy,
/// based on a given PersistenceExceptionTranslator.
/// </summary>
/// <author>Rod Johnson</author>
/// <author>Juergen Hoeller</author>
/// <author>Mark Pollack (.NET)</author>
public class PersistenceExceptionTranslationAdvisor : AbstractPointcutAdvisor
{
private PersistenceExceptionTranslationInterceptor advice;
private AttributeMatchingPointcut pointcut;
/// <summary>
/// Initializes a new instance of the <see cref="PersistenceExceptionTranslationAdvisor"/> class.
/// </summary>
/// <param name="persistenceExceptionTranslator">The persistence exception translator to use.</param>
/// <param name="repositoryAttributeType">Type of the repository attribute to check for.</param>
public PersistenceExceptionTranslationAdvisor(IPersistenceExceptionTranslator persistenceExceptionTranslator,
Type repositoryAttributeType)
{
this.advice = new PersistenceExceptionTranslationInterceptor(persistenceExceptionTranslator);
this.pointcut = new AttributeMatchingPointcut(repositoryAttributeType, true);
}
/// <summary>
/// Initializes a new instance of the <see cref="PersistenceExceptionTranslationAdvisor"/> class.
/// </summary>
/// <param name="objectFactory">The object factory to obtain all IPersistenceExceptionTranslators from.</param>
/// <param name="repositoryAttributeType">Type of the repository attribute to check for.</param>
public PersistenceExceptionTranslationAdvisor(IListableObjectFactory objectFactory, Type repositoryAttributeType)
{
this.advice = new PersistenceExceptionTranslationInterceptor(objectFactory);
this.pointcut = new AttributeMatchingPointcut(repositoryAttributeType, true);
}
/// <summary>
/// Return the advice part of this aspect.
/// </summary>
/// <value></value>
/// <remarks>
/// <p>
/// An advice may be an interceptor, a throws advice, before advice,
/// introduction etc.
/// </p>
/// </remarks>
/// <returns>
/// The advice that should apply if the pointcut matches.
/// </returns>
public override IAdvice Advice
{
get { return this.advice; }
set { }
}
/// <summary>
/// The <see cref="Spring.Aop.IPointcut"/> that drives this advisor.
/// </summary>
/// <value></value>
public override IPointcut Pointcut
{
get { return this.pointcut; }
set { }
}
}
}

View File

@@ -0,0 +1,176 @@
#region License
/*
* Copyright 2002-2008 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 Spring.Aop.Framework;
using Spring.Core;
using Spring.Dao;
using Spring.Dao.Support;
using Spring.Objects.Factory;
using Spring.Objects.Factory.Config;
using Spring.Stereotype;
using Spring.Util;
namespace Spring.Dao.Attributes
{
/// <summary>
/// Object post-processor that automatically applies persistence exception
/// translation to any bean that carries the <see cref="RepositoryAttribute"/>
/// attribute, adding a corresponding <see cref="PersistenceExceptionTranslationAdvisor"/>
/// to the xposed proxy (either an existing AOP proxy or a newly generated
/// proxy that implements all of the target's interfaces).
/// </summary>
/// <remarks>
/// <para>Translates native resource exceptions to Spring's <see cref="DataAccessException"/>
/// hierarchy. Autodetects object that implement the <see cref="IPersistenceExceptionTranslator"/>
/// interface, which are subsequently asked to translate candidate exceptions.
/// </para>
/// <para>All of Spring's applicable resource factories implement the
/// <code>IPersistenceExceptionTranslator</code> interface out of the box.
/// As a consequence, all that is usually needed to enable automatic exception
/// translation is marking all affected objects (such as DAOs) with the
/// <code>Repository</code> annotation, along with defining this post-processor
/// in the application context.
/// </para>
/// </remarks>
/// <author>Rod Johnson</author>
/// <author>Juergen Hoeller</author>
/// <author>Mark Pollack (.NET)</author>
/// <seealso cref="PersistenceExceptionTranslationAdvisor"/>
/// <seealso cref="RepositoryAttribute"/>
/// <seealso cref="DataAccessException"/>
/// <seealso cref="IPersistenceExceptionTranslator"/>
public class PersistenceExceptionTranslationPostProcessor : ProxyConfig, IObjectPostProcessor, IObjectFactoryAware, IOrdered
{
private Type repositoryAttributeType = typeof(RepositoryAttribute);
private PersistenceExceptionTranslationAdvisor persistenceExceptionTranslationAdvisor;
/// <summary>
/// Sets the type of the repository attribute. The default required attribute type is the
/// <see cref="RepositoryAttribute"/> attirbute. This setter property exists so that developers
/// can provide their own (non-Spring-specific) attribute type to indicate that a class has a
/// repository role.
/// </summary>
/// <value>The desitred type of the repository attribute.</value>
public Type RepositoryAttributeType
{
set
{
AssertUtils.ArgumentNotNull(value, "'RepositoryAttributeType' must not be null");
repositoryAttributeType = value;
}
}
public IObjectFactory ObjectFactory
{
set
{
IListableObjectFactory lof = value as IListableObjectFactory;
if (lof == null)
{
throw new ArgumentException("Cannot use PersistenceExceptionTranslator autodetection without IListableObjectFactory");
}
this.persistenceExceptionTranslationAdvisor =
new PersistenceExceptionTranslationAdvisor(lof, this.repositoryAttributeType);
}
}
public int Order
{
get {
return Int32.MaxValue;
// lowest precidence value
// This should run after all other post-processors, so that it can just add
// an advisor to existing proxies rather than double-proxy.
}
}
/// <summary>
/// Just return the passed in object instance
/// </summary>
/// <param name="instance">The new object instance.</param>
/// <param name="name">The name of the object.</param>
/// <returns>
/// The passed in object instance
/// </returns>
/// <exception cref="Spring.Objects.ObjectsException">
/// In case of errors.
/// </exception>
public object PostProcessBeforeInitialization(object instance, string name)
{
return instance;
}
/// <summary>
/// Add PersistenceExceptionTranslationAdvice to candidate object if it is a match.
/// Create AOP proxy if necessary or add advice to existing advice chain.
/// </summary>
/// <param name="instance">The new object instance.</param>
/// <param name="objectName">The name of the object.</param>
/// <returns>
/// The object instance to use, wrapped with either the original or a wrapped one.
/// </returns>
/// <exception cref="Spring.Objects.ObjectsException">
/// In case of errors.
/// </exception>
public object PostProcessAfterInitialization(object instance, string objectName)
{
IAdvised advised = instance as IAdvised;
Type targetType;
if (advised != null)
{
targetType = advised.TargetSource.TargetType;
} else
{
targetType = instance.GetType();
}
if (targetType == null)
{
// Can't do much here
return instance;
}
if (AopUtils.CanApply(this.persistenceExceptionTranslationAdvisor, targetType, ReflectionUtils.GetInterfaces(targetType)))
{
if (advised != null)
{
advised.AddAdvisor(this.persistenceExceptionTranslationAdvisor);
return instance;
}
else
{
ProxyFactory proxyFactory = new ProxyFactory(instance);
// copy our properties inherited from ProxyConfig
proxyFactory.CopyFrom(this);
proxyFactory.AddAdvisor(this.persistenceExceptionTranslationAdvisor);
return proxyFactory.GetProxy();
}
} else
{
return instance;
}
}
}
}

View File

@@ -0,0 +1,100 @@
#region License
/*
* Copyright 2002-2008 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 Spring.Data.Support;
using Spring.Util;
namespace Spring.Dao.Support
{
/// <summary>
/// Implementation of PersistenceExceptionTranslator that supports chaining,
/// allowing the addition of PersistenceExceptionTranslator instances in order.
/// Returns <code>non-null</code> on the first (if any) match.
/// </summary>
/// <author>Rod Johnson</author>
/// <author>Juergen Hoeller</author>
/// <author>Mark Pollack (.NET)</author>
public class ChainedPersistenceExceptionTranslator : IPersistenceExceptionTranslator
{
private readonly ArrayList translatorList = new ArrayList(4);
/// <summary>
/// Adds the translator to the translator list.
/// </summary>
/// <param name="translator">The translator.</param>
public void AddTranslator(IPersistenceExceptionTranslator translator)
{
AssertUtils.ArgumentNotNull(translator, "PersistenceExceptionTranslator must not be null");
this.translatorList.Add(translator);
}
/// <summary>
/// Gets all registered IPersistenceExceptionTranslator as an array.
/// </summary>
/// <value>The IPersistenceExceptionTranslators.</value>
public IPersistenceExceptionTranslator[] Translators
{
get
{
return (IPersistenceExceptionTranslator[]) translatorList.ToArray(typeof (IPersistenceExceptionTranslator));
}
}
/// <summary>
/// Translate the given exception thrown by a persistence framework to a
/// corresponding exception from Spring's generic DataAccessException hierarchy,
/// if possible.
/// </summary>
/// <param name="ex">The exception thrown.</param>
/// <returns>
/// the corresponding DataAccessException (or <code>null</code> if the
/// exception could not be translated, as in this case it may result from
/// user code rather than an actual persistence problem)
/// </returns>
/// <remarks>
/// <para>
/// Do not translate exceptions that are not understand by this translator:
/// for example, if coming from another persistence framework, or resulting
/// from user code and unrelated to persistence.
/// </para>
/// <para>
/// Of particular importance is the correct translation to <see cref="DataIntegrityViolationException"/>
/// for example on constraint violation. Implementations may use Spring ADO.NET Framework's
/// sophisticated exception translation to provide further information in the event of SQLException as a root cause.
/// </para>
/// </remarks>
/// <seealso cref="DataIntegrityViolationException"/>
/// <seealso cref="ErrorCodeExceptionTranslator"/>
public DataAccessException TranslateExceptionIfPossible(Exception ex)
{
DataAccessException translatedDex = null;
foreach (IPersistenceExceptionTranslator pet in translatorList)
{
translatedDex = pet.TranslateExceptionIfPossible(ex);
}
return translatedDex;
}
}
}

View File

@@ -0,0 +1,71 @@
#region License
/*
* Copyright 2002-2008 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 Spring.Dao;
using Spring.Data.Support;
namespace Spring.Dao.Support
{
/// <summary>
/// Interface implemented by Spring integrations with data access technologies
/// that throw exceptions.
/// </summary>
/// <remarks>
/// This allows consistent usage of combined exception translation functionality,
/// without forcing a single translator to understand every single possible type
/// of exception.
/// </remarks>
/// <author>Rod Johnson</author>
/// <author>Mark Pollack (.NET)</author>
public interface IPersistenceExceptionTranslator
{
/// <summary>
/// Translate the given exception thrown by a persistence framework to a
/// corresponding exception from Spring's generic DataAccessException hierarchy,
/// if possible.
/// </summary>
/// <remarks>
/// <para>
/// Do not translate exceptions that are not understand by this translator:
/// for example, if coming from another persistence framework, or resulting
/// from user code and unrelated to persistence.
/// </para>
/// <para>
/// Of particular importance is the correct translation to <see cref="DataIntegrityViolationException"/>
/// for example on constraint violation. Implementations may use Spring ADO.NET Framework's
/// sophisticated exception translation to provide further information in the event of SQLException as a root cause.
/// </para>
/// </remarks>
/// <param name="ex">The exception thrown.</param>
/// <returns>the corresponding DataAccessException (or <code>null</code> if the
/// exception could not be translated, as in this case it may result from
/// user code rather than an actual persistence problem)
/// </returns>
/// <seealso cref="DataIntegrityViolationException"/>
/// <seealso cref="ErrorCodeExceptionTranslator"/>
/// <author>Rod Johnson</author>
/// <author>Juergen Hoeller</author>
/// <author>Mark Pollack (.NET)</author>
DataAccessException TranslateExceptionIfPossible(Exception ex);
}
}

View File

@@ -0,0 +1,195 @@
#region License
/*
* Copyright 2002-2008 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 AopAlliance.Intercept;
using Spring.Objects.Factory;
using Spring.Objects.Factory.Config;
using Spring.Util;
namespace Spring.Dao.Support
{
/// <summary>
/// AOP MethodInterceptor that provides persistence exception translation
/// based on a given PersistenceExceptionTranslator.
/// </summary>
/// <remarks>
/// Delegates to the given <see cref="IPersistenceExceptionTranslator"/> to translate
/// an Exception thrown into Spring's DataAccessException hierarchy
/// (if appropriate).
/// </remarks>
/// <author>Rod Johnson</author>
/// <author>Juergen Hoeller</author>
/// <author>Mark Pollack (.NET)</author>
public class PersistenceExceptionTranslationInterceptor : IMethodInterceptor, IObjectFactoryAware, IInitializingObject
{
private IPersistenceExceptionTranslator persistenceExceptionTranslator;
/// <summary>
/// Initializes a new instance of the <see cref="PersistenceExceptionTranslationInterceptor"/> class.
/// Needs to be configured with a PersistenceExceptionTranslator afterwards.
/// </summary>
public PersistenceExceptionTranslationInterceptor()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PersistenceExceptionTranslationInterceptor"/> class for the
/// given IPersistenceExceptionTranslator
/// </summary>
/// <param name="persistenceExceptionTranslator">The persistence exception translator to use.</param>
public PersistenceExceptionTranslationInterceptor(IPersistenceExceptionTranslator persistenceExceptionTranslator)
{
this.persistenceExceptionTranslator = persistenceExceptionTranslator;
}
/// <summary>
/// Initializes a new instance of the <see cref="PersistenceExceptionTranslationInterceptor"/> class, autodetecting
/// IPersistenceExceptionTranslators in the given object factory.
/// </summary>
/// <param name="objectFactory">The object factory to obtain all IPersistenceExceptionTranslators from.</param>
public PersistenceExceptionTranslationInterceptor(IListableObjectFactory objectFactory)
{
this.persistenceExceptionTranslator = DetectPersistenceExceptionTranslators(objectFactory);
}
/// <summary>
/// Sets the persistence exception translator. The default is to autodetect all IPersistenceExceptionTranslators
/// in the containing object factory, using them in a chain.
/// </summary>
/// <value>The persistence exception translator.</value>
public IPersistenceExceptionTranslator PersistenceExceptionTranslator
{
set
{
AssertUtils.ArgumentNotNull(value, "IPersistenceExceptionTranslator must not be null");
persistenceExceptionTranslator = value;
}
}
/// <summary>
/// Callback that supplies the owning factory to an object instance.
/// </summary>
/// <value>
/// Owning <see cref="Spring.Objects.Factory.IObjectFactory"/>
/// (may not be <see langword="null"/>). The object can immediately
/// call methods on the factory.
/// </value>
/// <remarks>
/// <p>
/// Invoked after population of normal object properties but before an init
/// callback like <see cref="Spring.Objects.Factory.IInitializingObject"/>'s
/// <see cref="Spring.Objects.Factory.IInitializingObject.AfterPropertiesSet"/>
/// method or a custom init-method.
/// </p>
/// </remarks>
/// <exception cref="Spring.Objects.ObjectsException">
/// In case of initialization errors.
/// </exception>
public IObjectFactory ObjectFactory
{
set
{
if (this.persistenceExceptionTranslator == null)
{
// No explicit exception translator specified - perform autodetection.
IListableObjectFactory owningFactory = value as IListableObjectFactory;
if (owningFactory == null)
{
throw new ArgumentException("Cannot use IPersistenceExceptionTranslator autodetection without IListableBeanFactory");
}
this.persistenceExceptionTranslator = DetectPersistenceExceptionTranslators(owningFactory);
}
}
}
/// <summary>
/// Ensures that the property PersistenceExceptionTranslator has been set.
/// Invoked by an <see cref="Spring.Objects.Factory.IObjectFactory"/>
/// after it has injected all of an object's dependencies.
/// </summary>
/// <exception cref="System.Exception">
/// In the event of misconfiguration (such as the failure to set a
/// required property) or if initialization fails.
/// </exception>
public void AfterPropertiesSet()
{
if (this.persistenceExceptionTranslator == null)
{
throw new ArgumentException("Property 'PersistenceExceptionTranslator' is required");
}
}
/// <summary>
/// Detects the petsistence exception translators in the given object factory.
/// </summary>
/// <param name="objectFactory">The object factory for obtaining all IPersistenceExceptionTranslators.</param>
/// <returns>A chained IPersistenceExceptionTranslator, combining all PersistenceExceptionTranslators found in the factory
/// </returns>
/// <seealso cref="ChainedPersistenceExceptionTranslator"/>
protected IPersistenceExceptionTranslator DetectPersistenceExceptionTranslators(IListableObjectFactory objectFactory)
{
// Find all translators, being careful not to activate FactoryObjects.
IDictionary pets =
ObjectFactoryUtils.ObjectsOfTypeIncludingAncestors(objectFactory,
typeof (IPersistenceExceptionTranslator), false,
false);
if (pets.Count == 0)
{
throw new InvalidOperationException("No persistence exception translators found in container. Cannot perform exception translation.");
}
ChainedPersistenceExceptionTranslator cpet = new ChainedPersistenceExceptionTranslator();
foreach (DictionaryEntry pet in pets)
{
cpet.AddTranslator((IPersistenceExceptionTranslator)pet.Value);
}
return cpet;
}
/// <summary>
/// Return a translated exception if this is appropriate, otherwise rethrow the original exception.
/// </summary>
/// <param name="invocation"></param>
/// <returns></returns>
public object Invoke(IMethodInvocation invocation)
{
try
{
return invocation.Proceed();
} catch (Exception ex)
{
DataAccessException dex = this.persistenceExceptionTranslator.TranslateExceptionIfPossible(ex);
if (dex == null)
{
throw;
} else
{
throw dex;
}
}
}
}
}

View File

@@ -97,6 +97,8 @@
<SubType>Code</SubType>
</Compile>
<Compile Include="AssemblyInfo.cs" />
<Compile Include="Dao\Attributes\PersistenceExceptionTranslationAdvisor.cs" />
<Compile Include="Dao\Attributes\PersistenceExceptionTranslationPostProcessor.cs" />
<Compile Include="Dao\CannotAcquireLockException.cs" />
<Compile Include="Dao\CannotSerializeTransactionException.cs" />
<Compile Include="Dao\CleanupFailureDataAccessException.cs" />
@@ -116,9 +118,12 @@
<Compile Include="Dao\ObjectRetrievalFailureException.cs" />
<Compile Include="Dao\OptimisticLockingFailureException.cs" />
<Compile Include="Dao\PermissionDeniedDataAccessException.cs" />
<Compile Include="Dao\Support\ChainedPersistenceExceptionTranslator.cs" />
<Compile Include="Dao\Support\DaoSupport.cs" />
<Compile Include="Dao\Support\DataAccessUtils.cs" />
<Compile Include="Dao\Support\Generic\DataAccessUtils.cs" />
<Compile Include="Dao\Support\IPersistenceExceptionTranslator.cs" />
<Compile Include="Dao\Support\PersistenceExceptionTranslationInterceptor.cs" />
<Compile Include="Dao\TypeMismatchDataAccessException.cs" />
<Compile Include="Dao\UncategorizedDataAccessException.cs" />
<Compile Include="Data\BadSqlGrammarException.cs" />

View File

@@ -0,0 +1,83 @@
#region License
/*
* Copyright <20> 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
#region Imports
using System;
using NUnit.Framework;
using Spring.Stereotype;
#endregion
namespace Spring.Dao.Attributes
{
/// <summary>
/// This class contains tests for
/// </summary>
/// <author>Mark Pollack</author>
/// <version>$Id:$</version>
[TestFixture]
public class PersistenceExceptionTranslationAdvisorTests
{
[SetUp]
public void Setup()
{
}
[Test]
public void Test()
{
}
}
#if !NET_1_0
[Repository]
public class StereotypedRepositoryInterfaceImpl : RepositoryInterfaceImpl
{
// Extends above class just to add repository annotation
}
public class RepositoryInterfaceImpl : IRepositoryInterface
{
private Exception ex;
public Exception Behavior
{
set { ex = value; }
}
public void Throws()
{
if (ex != null)
{
throw ex;
}
}
}
#endif
public interface IRepositoryInterface
{
void Throws();
}
}

View File

@@ -0,0 +1,102 @@
#region License
/*
* Copyright <20> 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
#if !NET_1_0
#region Imports
using NUnit.Framework;
using Spring.Aop;
using Spring.Aop.Framework;
using Spring.Context.Support;
using Spring.Dao.Support;
using Spring.Objects;
using Spring.Objects.Factory.Support;
#endregion
namespace Spring.Dao.Attributes
{
/// <summary>
/// Unit tests for PersistenceExceptionTranslationPostProcessor. Does not test
/// translation; there are separate unit tests for the Spring AOP Advisor.
/// Just checks whether proxying occurs correctly,
/// </summary>
/// <author>Rod Johnson</author>
/// <author>Mark Pollack (.NET)</author>
/// <version>$Id:$</version>
[TestFixture]
public class PersistenceExceptionTranslationPostProcessorTests
{
[SetUp]
public void Setup()
{
}
[Test]
public void FailsWithNoPersistenceExceptionTranslators()
{
GenericApplicationContext gac = new GenericApplicationContext();
gac.RegisterObjectDefinition("translator", new RootObjectDefinition(typeof(PersistenceExceptionTranslationPostProcessor)));
gac.RegisterObjectDefinition("proxied", new RootObjectDefinition(typeof(StereotypedRepositoryInterfaceImpl)));
try
{
gac.Refresh();
Assert.Fail("Should fail with no translators");
} catch (ObjectsException)
{
// expected.
}
}
[Test]
public void ProxiesCorrectly()
{
GenericApplicationContext gac = new GenericApplicationContext();
gac.RegisterObjectDefinition("translator", new RootObjectDefinition(typeof(PersistenceExceptionTranslationPostProcessor)));
gac.RegisterObjectDefinition("notProxied", new RootObjectDefinition(typeof(RepositoryInterfaceImpl)));
gac.RegisterObjectDefinition("proxied",
new RootObjectDefinition(typeof (StereotypedRepositoryInterfaceImpl)));
gac.RegisterObjectDefinition("chainedTranslator",
new RootObjectDefinition(typeof (ChainedPersistenceExceptionTranslator)));
gac.Refresh();
IRepositoryInterface shouldNotBeProxied = (IRepositoryInterface) gac.GetObject("notProxied");
Assert.IsFalse(AopUtils.IsAopProxy(shouldNotBeProxied));
IRepositoryInterface shouldBeProxied = (IRepositoryInterface) gac.GetObject("proxied");
Assert.IsTrue(AopUtils.IsAopProxy(shouldBeProxied));
CheckWillTranslateExceptions(shouldBeProxied);
}
private void CheckWillTranslateExceptions(object o)
{
IAdvised advised = o as IAdvised;
Assert.IsNotNull(advised);
foreach (IAdvisor advisor in advised.Advisors)
{
PersistenceExceptionTranslationAdvisor peta = advisor as PersistenceExceptionTranslationAdvisor;
if (peta != null) return;
}
Assert.Fail("No translation");
}
}
}
#endif

View File

@@ -170,6 +170,8 @@
<EmbeddedResource Include="Transaction\Config\TxNamespaceParserTests.xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Dao\Attributes\PersistenceExceptionTranslationAdvisorTests.cs" />
<Compile Include="Dao\Attributes\PersistenceExceptionTranslationPostProcessorTests.cs" />
<Compile Include="Data\Common\DbParametersTests.cs" />
<Compile Include="Data\Common\UserCredentialsDbProviderTests.cs" />
<Compile Include="Data\Core\ServiceDomainTransactionManagerTests.cs" />