diff --git a/src/Spring/Spring.Aop/Aop/Support/AttributeMatchingPointcut.cs b/src/Spring/Spring.Aop/Aop/Support/AttributeMatchingPointcut.cs
new file mode 100644
index 00000000..12940a80
--- /dev/null
+++ b/src/Spring/Spring.Aop/Aop/Support/AttributeMatchingPointcut.cs
@@ -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
+{
+ ///
+ /// Pointcut that looks for a specific attribute being present on a class or
+ /// method.
+ ///
+ /// Juergen Hoeller
+ /// Mark Pollack (.NET)
+ public class AttributeMatchingPointcut : IPointcut
+ {
+ private readonly ITypeFilter typeFilter;
+ private readonly IMethodMatcher methodMatcher;
+
+
+ ///
+ /// Initializes a new instance of the class for the
+ /// given attribute type.
+ ///
+ /// Type of the attribute to look for at the class level.
+ public AttributeMatchingPointcut(Type attributeType)
+ {
+ ValidateAttributeTypeArgument(attributeType);
+ this.typeFilter = new AttributeTypeFilter(attributeType);
+ this.methodMatcher = TrueMethodMatcher.True;
+ }
+
+ ///
+ /// Initializes a new instance of the class for the
+ /// given attribute type
+ ///
+ /// Type of the attribute.
+ /// if set to true [check inherited].
+ public AttributeMatchingPointcut(Type attributeType, bool checkInherited)
+ {
+ ValidateAttributeTypeArgument(attributeType);
+ this.typeFilter = new AttributeTypeFilter(attributeType, checkInherited);
+ this.methodMatcher = TrueMethodMatcher.True;
+ }
+
+ ///
+ /// Initializes a new instance of the class for the given
+ /// attribute type
+ ///
+ /// The attribute type to look for at the class level.
+ /// The attribute type to look for at the method attribute.
+ 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;
+ }
+ }
+
+ ///
+ /// The for this pointcut.
+ ///
+ /// The current .
+ public ITypeFilter TypeFilter
+ {
+ get { return this.typeFilter; }
+ }
+
+ ///
+ /// The for this pointcut.
+ ///
+ /// The current .
+ 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));
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Spring/Spring.Aop/Aop/Support/AttributeMethodMatcher.cs b/src/Spring/Spring.Aop/Aop/Support/AttributeMethodMatcher.cs
new file mode 100644
index 00000000..989ae74c
--- /dev/null
+++ b/src/Spring/Spring.Aop/Aop/Support/AttributeMethodMatcher.cs
@@ -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
+{
+ ///
+ /// 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
+ ///
+ /// Juergen hoeller
+ /// Mark Pollack
+ ///
+ public class AttributeMethodMatcher : StaticMethodMatcher
+ {
+ private readonly Type attributeType;
+
+
+ ///
+ /// Initializes a new instance of the class for the
+ /// given atribute type.
+ ///
+ /// Type of the attribute to look for.
+ public AttributeMethodMatcher(Type attributeType)
+ {
+ ValidateAttributeTypeArgument(attributeType);
+ this.attributeType = attributeType;
+ }
+
+ ///
+ /// Does the supplied satisfy this matcher?
+ ///
+ /// The candidate method.
+ /// The target (may be ,
+ /// in which case the candidate must be taken
+ /// to be the 's declaring class).
+ ///
+ /// if this this method matches statically.
+ ///
+ ///
+ ///
+ /// Must be implemented by a derived class in order to specify matching
+ /// rules.
+ ///
+ ///
+ 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));
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Spring/Spring.Aop/Aop/Support/AttributeTypeFilter.cs b/src/Spring/Spring.Aop/Aop/Support/AttributeTypeFilter.cs
new file mode 100644
index 00000000..9a2d6f71
--- /dev/null
+++ b/src/Spring/Spring.Aop/Aop/Support/AttributeTypeFilter.cs
@@ -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
+{
+ ///
+ /// ITypeFilter that looks for a specific attribute being present on a class
+ ///
+ /// Juergen Hoeller
+ /// Mark Pollack (.NET)
+ public class AttributeTypeFilter : ITypeFilter
+ {
+ private readonly Type attributeType;
+
+ private readonly bool checkInherited;
+
+ ///
+ /// Initializes a new instance of the class for the
+ /// given attribute type.
+ ///
+ /// Type of the attribute to look for.
+ public AttributeTypeFilter(Type attributeType) : this(attributeType, false)
+ {
+
+ }
+
+ ///
+ /// Initializes a new instance of the class for the
+ /// given attribute type.
+ ///
+ /// Type of the attribute.
+ /// if set to true [check inherited].
+ 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;
+ }
+
+
+ ///
+ /// Should the pointcut apply to the supplied ?
+ ///
+ /// The candidate .
+ ///
+ /// if the advice should apply to the supplied
+ ///
+ ///
+ 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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Spring/Spring.Aop/Spring.Aop.2005.csproj b/src/Spring/Spring.Aop/Spring.Aop.2005.csproj
index 59be97d4..5aa4e8ad 100644
--- a/src/Spring/Spring.Aop/Spring.Aop.2005.csproj
+++ b/src/Spring/Spring.Aop/Spring.Aop.2005.csproj
@@ -281,12 +281,15 @@
Code
+
Code
Code
+
+
Code
diff --git a/src/Spring/Spring.Core/Spring.Core.2005.csproj b/src/Spring/Spring.Core/Spring.Core.2005.csproj
index ea4ae66b..10620982 100644
--- a/src/Spring/Spring.Core/Spring.Core.2005.csproj
+++ b/src/Spring/Spring.Core/Spring.Core.2005.csproj
@@ -956,6 +956,9 @@
+
+
+
diff --git a/src/Spring/Spring.Core/Stereotype/ComponentAttribute.cs b/src/Spring/Spring.Core/Stereotype/ComponentAttribute.cs
new file mode 100644
index 00000000..0b8c4d29
--- /dev/null
+++ b/src/Spring/Spring.Core/Stereotype/ComponentAttribute.cs
@@ -0,0 +1,54 @@
+
+
+using System;
+
+namespace Spring.Stereotype {
+
+ ///
+ /// 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.
+ ///
+ /// Other class-level annotations may be considered as identifying
+ /// a component as well, typically a special kind of component:
+ /// e.g. the Repository attribute.
+ ///
+ /// Mark Fisher
+ /// Mark Pollack (.NET)
+ ///
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, Inherited = true)]
+ [Serializable]
+ public class ComponentAttribute : Attribute
+ {
+ private string name = "";
+
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ComponentAttribute()
+ {
+ }
+
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The name of the component.
+ public ComponentAttribute(string name)
+ {
+ this.name = name;
+ }
+
+
+ ///
+ /// Gets or sets the name of the component
+ ///
+ /// The name of the component.
+ public string Name
+ {
+ get { return name; }
+ set { name = value; }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Spring/Spring.Core/Stereotype/RepositoryAttribute.cs b/src/Spring/Spring.Core/Stereotype/RepositoryAttribute.cs
new file mode 100644
index 00000000..89dc4fbf
--- /dev/null
+++ b/src/Spring/Spring.Core/Stereotype/RepositoryAttribute.cs
@@ -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
+{
+ ///
+ /// Indicates that an annotated class is a "Repository" (or "DAO").
+ ///
+ ///
+ /// 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.
+ ///
+ /// This attribute also serves as a specialization of the ComponentAttribute, allowing implementation
+ /// classes to be autodetected in future releases through assembly scanning.
+ ///
+ ///
+ /// Rod Johnson
+ /// Jueren Hoeller
+ /// Mark Pollack (.NET)
+ ///
+ public class RepositoryAttribute : ComponentAttribute
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public RepositoryAttribute()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The name of the repository.
+ public RepositoryAttribute(string name) : base(name)
+ {
+ }
+
+
+ }
+}
\ No newline at end of file
diff --git a/src/Spring/Spring.Core/Stereotype/ServiceAttribute.cs b/src/Spring/Spring.Core/Stereotype/ServiceAttribute.cs
new file mode 100644
index 00000000..3dcd334d
--- /dev/null
+++ b/src/Spring/Spring.Core/Stereotype/ServiceAttribute.cs
@@ -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
+{
+ ///
+ /// Indicates that an annotated class is a "Service" (e.g. a business service facade).
+ ///
+ ///
+ ///
+ /// This attribute also serves as a specialization of the ComponentAttribute, allowing implementation
+ /// classes to be autodetected in future releases through assembly scanning.
+ ///
+ ///
+ /// Juergen Hoeller
+ /// Mark Pollack (.NET)
+ public class ServiceAttribute : ComponentAttribute
+ {
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ServiceAttribute()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The name.
+ public ServiceAttribute(string name) : base(name)
+ {
+ }
+
+
+ }
+}
\ No newline at end of file
diff --git a/src/Spring/Spring.Data/Dao/Attributes/PersistenceExceptionTranslationAdvisor.cs b/src/Spring/Spring.Data/Dao/Attributes/PersistenceExceptionTranslationAdvisor.cs
new file mode 100644
index 00000000..25ce1e35
--- /dev/null
+++ b/src/Spring/Spring.Data/Dao/Attributes/PersistenceExceptionTranslationAdvisor.cs
@@ -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
+{
+ ///
+ /// 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.
+ ///
+ /// Rod Johnson
+ /// Juergen Hoeller
+ /// Mark Pollack (.NET)
+ public class PersistenceExceptionTranslationAdvisor : AbstractPointcutAdvisor
+ {
+ private PersistenceExceptionTranslationInterceptor advice;
+
+ private AttributeMatchingPointcut pointcut;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The persistence exception translator to use.
+ /// Type of the repository attribute to check for.
+ public PersistenceExceptionTranslationAdvisor(IPersistenceExceptionTranslator persistenceExceptionTranslator,
+ Type repositoryAttributeType)
+ {
+ this.advice = new PersistenceExceptionTranslationInterceptor(persistenceExceptionTranslator);
+ this.pointcut = new AttributeMatchingPointcut(repositoryAttributeType, true);
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The object factory to obtain all IPersistenceExceptionTranslators from.
+ /// Type of the repository attribute to check for.
+ public PersistenceExceptionTranslationAdvisor(IListableObjectFactory objectFactory, Type repositoryAttributeType)
+ {
+ this.advice = new PersistenceExceptionTranslationInterceptor(objectFactory);
+ this.pointcut = new AttributeMatchingPointcut(repositoryAttributeType, true);
+ }
+
+ ///
+ /// Return the advice part of this aspect.
+ ///
+ ///
+ ///
+ ///
+ /// An advice may be an interceptor, a throws advice, before advice,
+ /// introduction etc.
+ ///
+ ///
+ ///
+ /// The advice that should apply if the pointcut matches.
+ ///
+ public override IAdvice Advice
+ {
+ get { return this.advice; }
+ set { }
+ }
+
+ ///
+ /// The that drives this advisor.
+ ///
+ ///
+ public override IPointcut Pointcut
+ {
+ get { return this.pointcut; }
+ set { }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Spring/Spring.Data/Dao/Attributes/PersistenceExceptionTranslationPostProcessor.cs b/src/Spring/Spring.Data/Dao/Attributes/PersistenceExceptionTranslationPostProcessor.cs
new file mode 100644
index 00000000..7e589b1e
--- /dev/null
+++ b/src/Spring/Spring.Data/Dao/Attributes/PersistenceExceptionTranslationPostProcessor.cs
@@ -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
+{
+ ///
+ /// Object post-processor that automatically applies persistence exception
+ /// translation to any bean that carries the
+ /// attribute, adding a corresponding
+ /// to the xposed proxy (either an existing AOP proxy or a newly generated
+ /// proxy that implements all of the target's interfaces).
+ ///
+ ///
+ /// Translates native resource exceptions to Spring's
+ /// hierarchy. Autodetects object that implement the
+ /// interface, which are subsequently asked to translate candidate exceptions.
+ ///
+ /// All of Spring's applicable resource factories implement the
+ /// IPersistenceExceptionTranslator 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
+ /// Repository annotation, along with defining this post-processor
+ /// in the application context.
+ ///
+ ///
+ /// Rod Johnson
+ /// Juergen Hoeller
+ /// Mark Pollack (.NET)
+ ///
+ ///
+ ///
+ ///
+ public class PersistenceExceptionTranslationPostProcessor : ProxyConfig, IObjectPostProcessor, IObjectFactoryAware, IOrdered
+ {
+ private Type repositoryAttributeType = typeof(RepositoryAttribute);
+
+ private PersistenceExceptionTranslationAdvisor persistenceExceptionTranslationAdvisor;
+
+
+ ///
+ /// Sets the type of the repository attribute. The default required attribute type is the
+ /// 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.
+ ///
+ /// The desitred type of the repository attribute.
+ 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.
+ }
+ }
+
+ ///
+ /// Just return the passed in object instance
+ ///
+ /// The new object instance.
+ /// The name of the object.
+ ///
+ /// The passed in object instance
+ ///
+ ///
+ /// In case of errors.
+ ///
+ public object PostProcessBeforeInitialization(object instance, string name)
+ {
+ return instance;
+ }
+
+ ///
+ /// Add PersistenceExceptionTranslationAdvice to candidate object if it is a match.
+ /// Create AOP proxy if necessary or add advice to existing advice chain.
+ ///
+ /// The new object instance.
+ /// The name of the object.
+ ///
+ /// The object instance to use, wrapped with either the original or a wrapped one.
+ ///
+ ///
+ /// In case of errors.
+ ///
+ 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;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Spring/Spring.Data/Dao/Support/ChainedPersistenceExceptionTranslator.cs b/src/Spring/Spring.Data/Dao/Support/ChainedPersistenceExceptionTranslator.cs
new file mode 100644
index 00000000..76b5c74c
--- /dev/null
+++ b/src/Spring/Spring.Data/Dao/Support/ChainedPersistenceExceptionTranslator.cs
@@ -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
+{
+ ///
+ /// Implementation of PersistenceExceptionTranslator that supports chaining,
+ /// allowing the addition of PersistenceExceptionTranslator instances in order.
+ /// Returns non-null on the first (if any) match.
+ ///
+ /// Rod Johnson
+ /// Juergen Hoeller
+ /// Mark Pollack (.NET)
+ public class ChainedPersistenceExceptionTranslator : IPersistenceExceptionTranslator
+ {
+ private readonly ArrayList translatorList = new ArrayList(4);
+
+ ///
+ /// Adds the translator to the translator list.
+ ///
+ /// The translator.
+ public void AddTranslator(IPersistenceExceptionTranslator translator)
+ {
+ AssertUtils.ArgumentNotNull(translator, "PersistenceExceptionTranslator must not be null");
+ this.translatorList.Add(translator);
+ }
+
+
+ ///
+ /// Gets all registered IPersistenceExceptionTranslator as an array.
+ ///
+ /// The IPersistenceExceptionTranslators.
+ public IPersistenceExceptionTranslator[] Translators
+ {
+ get
+ {
+ return (IPersistenceExceptionTranslator[]) translatorList.ToArray(typeof (IPersistenceExceptionTranslator));
+ }
+ }
+
+ ///
+ /// Translate the given exception thrown by a persistence framework to a
+ /// corresponding exception from Spring's generic DataAccessException hierarchy,
+ /// if possible.
+ ///
+ /// The exception thrown.
+ ///
+ /// the corresponding DataAccessException (or null if the
+ /// exception could not be translated, as in this case it may result from
+ /// user code rather than an actual persistence problem)
+ ///
+ ///
+ ///
+ /// 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.
+ ///
+ ///
+ /// Of particular importance is the correct translation to
+ /// 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.
+ ///
+ ///
+ ///
+ ///
+ public DataAccessException TranslateExceptionIfPossible(Exception ex)
+ {
+ DataAccessException translatedDex = null;
+ foreach (IPersistenceExceptionTranslator pet in translatorList)
+ {
+ translatedDex = pet.TranslateExceptionIfPossible(ex);
+ }
+ return translatedDex;
+ }
+
+
+ }
+}
\ No newline at end of file
diff --git a/src/Spring/Spring.Data/Dao/Support/IPersistenceExceptionTranslator.cs b/src/Spring/Spring.Data/Dao/Support/IPersistenceExceptionTranslator.cs
new file mode 100644
index 00000000..67921199
--- /dev/null
+++ b/src/Spring/Spring.Data/Dao/Support/IPersistenceExceptionTranslator.cs
@@ -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
+{
+ ///
+ /// Interface implemented by Spring integrations with data access technologies
+ /// that throw exceptions.
+ ///
+ ///
+ /// This allows consistent usage of combined exception translation functionality,
+ /// without forcing a single translator to understand every single possible type
+ /// of exception.
+ ///
+ /// Rod Johnson
+ /// Mark Pollack (.NET)
+ public interface IPersistenceExceptionTranslator
+ {
+ ///
+ /// Translate the given exception thrown by a persistence framework to a
+ /// corresponding exception from Spring's generic DataAccessException hierarchy,
+ /// if possible.
+ ///
+ ///
+ ///
+ /// 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.
+ ///
+ ///
+ /// Of particular importance is the correct translation to
+ /// 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.
+ ///
+ ///
+ /// The exception thrown.
+ /// the corresponding DataAccessException (or null if the
+ /// exception could not be translated, as in this case it may result from
+ /// user code rather than an actual persistence problem)
+ ///
+ ///
+ ///
+ /// Rod Johnson
+ /// Juergen Hoeller
+ /// Mark Pollack (.NET)
+ DataAccessException TranslateExceptionIfPossible(Exception ex);
+ }
+}
\ No newline at end of file
diff --git a/src/Spring/Spring.Data/Dao/Support/PersistenceExceptionTranslationInterceptor.cs b/src/Spring/Spring.Data/Dao/Support/PersistenceExceptionTranslationInterceptor.cs
new file mode 100644
index 00000000..ffcc949c
--- /dev/null
+++ b/src/Spring/Spring.Data/Dao/Support/PersistenceExceptionTranslationInterceptor.cs
@@ -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
+{
+ ///
+ /// AOP MethodInterceptor that provides persistence exception translation
+ /// based on a given PersistenceExceptionTranslator.
+ ///
+ ///
+ /// Delegates to the given to translate
+ /// an Exception thrown into Spring's DataAccessException hierarchy
+ /// (if appropriate).
+ ///
+ /// Rod Johnson
+ /// Juergen Hoeller
+ /// Mark Pollack (.NET)
+ public class PersistenceExceptionTranslationInterceptor : IMethodInterceptor, IObjectFactoryAware, IInitializingObject
+ {
+ private IPersistenceExceptionTranslator persistenceExceptionTranslator;
+
+
+ ///
+ /// Initializes a new instance of the class.
+ /// Needs to be configured with a PersistenceExceptionTranslator afterwards.
+ ///
+ public PersistenceExceptionTranslationInterceptor()
+ {
+ }
+
+
+ ///
+ /// Initializes a new instance of the class for the
+ /// given IPersistenceExceptionTranslator
+ ///
+ /// The persistence exception translator to use.
+ public PersistenceExceptionTranslationInterceptor(IPersistenceExceptionTranslator persistenceExceptionTranslator)
+ {
+ this.persistenceExceptionTranslator = persistenceExceptionTranslator;
+ }
+
+ ///
+ /// Initializes a new instance of the class, autodetecting
+ /// IPersistenceExceptionTranslators in the given object factory.
+ ///
+ /// The object factory to obtain all IPersistenceExceptionTranslators from.
+ public PersistenceExceptionTranslationInterceptor(IListableObjectFactory objectFactory)
+ {
+ this.persistenceExceptionTranslator = DetectPersistenceExceptionTranslators(objectFactory);
+ }
+
+
+ ///
+ /// Sets the persistence exception translator. The default is to autodetect all IPersistenceExceptionTranslators
+ /// in the containing object factory, using them in a chain.
+ ///
+ /// The persistence exception translator.
+ public IPersistenceExceptionTranslator PersistenceExceptionTranslator
+ {
+ set
+ {
+ AssertUtils.ArgumentNotNull(value, "IPersistenceExceptionTranslator must not be null");
+ persistenceExceptionTranslator = value;
+ }
+ }
+
+
+ ///
+ /// Callback that supplies the owning factory to an object instance.
+ ///
+ ///
+ /// Owning
+ /// (may not be ). The object can immediately
+ /// call methods on the factory.
+ ///
+ ///
+ ///
+ /// Invoked after population of normal object properties but before an init
+ /// callback like 's
+ ///
+ /// method or a custom init-method.
+ ///
+ ///
+ ///
+ /// In case of initialization errors.
+ ///
+ 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);
+ }
+ }
+ }
+
+ ///
+ /// Ensures that the property PersistenceExceptionTranslator has been set.
+ /// Invoked by an
+ /// after it has injected all of an object's dependencies.
+ ///
+ ///
+ /// In the event of misconfiguration (such as the failure to set a
+ /// required property) or if initialization fails.
+ ///
+ public void AfterPropertiesSet()
+ {
+ if (this.persistenceExceptionTranslator == null)
+ {
+ throw new ArgumentException("Property 'PersistenceExceptionTranslator' is required");
+ }
+ }
+
+ ///
+ /// Detects the petsistence exception translators in the given object factory.
+ ///
+ /// The object factory for obtaining all IPersistenceExceptionTranslators.
+ /// A chained IPersistenceExceptionTranslator, combining all PersistenceExceptionTranslators found in the factory
+ ///
+ ///
+ 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;
+ }
+
+ ///
+ /// Return a translated exception if this is appropriate, otherwise rethrow the original exception.
+ ///
+ ///
+ ///
+ 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;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Spring/Spring.Data/Spring.Data.2005.csproj b/src/Spring/Spring.Data/Spring.Data.2005.csproj
index d205235d..20050270 100644
--- a/src/Spring/Spring.Data/Spring.Data.2005.csproj
+++ b/src/Spring/Spring.Data/Spring.Data.2005.csproj
@@ -97,6 +97,8 @@
Code
+
+
@@ -116,9 +118,12 @@
+
+
+
diff --git a/test/Spring/Spring.Data.Tests/Dao/Attributes/PersistenceExceptionTranslationAdvisorTests.cs b/test/Spring/Spring.Data.Tests/Dao/Attributes/PersistenceExceptionTranslationAdvisorTests.cs
new file mode 100644
index 00000000..c2fc83f8
--- /dev/null
+++ b/test/Spring/Spring.Data.Tests/Dao/Attributes/PersistenceExceptionTranslationAdvisorTests.cs
@@ -0,0 +1,83 @@
+#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
+
+#region Imports
+
+using System;
+using NUnit.Framework;
+using Spring.Stereotype;
+
+#endregion
+
+namespace Spring.Dao.Attributes
+{
+ ///
+ /// This class contains tests for
+ ///
+ /// Mark Pollack
+ /// $Id:$
+ [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();
+ }
+
+}
\ No newline at end of file
diff --git a/test/Spring/Spring.Data.Tests/Dao/Attributes/PersistenceExceptionTranslationPostProcessorTests.cs b/test/Spring/Spring.Data.Tests/Dao/Attributes/PersistenceExceptionTranslationPostProcessorTests.cs
new file mode 100644
index 00000000..539e9ad1
--- /dev/null
+++ b/test/Spring/Spring.Data.Tests/Dao/Attributes/PersistenceExceptionTranslationPostProcessorTests.cs
@@ -0,0 +1,102 @@
+#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
+#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
+{
+ ///
+ /// Unit tests for PersistenceExceptionTranslationPostProcessor. Does not test
+ /// translation; there are separate unit tests for the Spring AOP Advisor.
+ /// Just checks whether proxying occurs correctly,
+ ///
+ /// Rod Johnson
+ /// Mark Pollack (.NET)
+ /// $Id:$
+ [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
\ No newline at end of file
diff --git a/test/Spring/Spring.Data.Tests/Spring.Data.Tests.2005.csproj b/test/Spring/Spring.Data.Tests/Spring.Data.Tests.2005.csproj
index 13c80e7d..6fc19ae9 100644
--- a/test/Spring/Spring.Data.Tests/Spring.Data.Tests.2005.csproj
+++ b/test/Spring/Spring.Data.Tests/Spring.Data.Tests.2005.csproj
@@ -170,6 +170,8 @@
+
+