#region License
/*
* Copyright 2002-2004 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 System.Collections.Specialized;
using Spring.Aop;
using Spring.Aop.Framework;
using Spring.Aop.Framework.Adapter;
using Spring.Aop.Framework.DynamicProxy;
using Spring.Aop.Support;
using Spring.Aop.Target;
using Spring.Core.TypeResolution;
using Spring.Objects.Factory;
#endregion
namespace Spring.Transaction.Interceptor{
/// <summary>
/// Proxy factory object for simplified declarative transaction handling.
/// </summary>
/// <remarks>
/// <p>
/// Alternative to the standard AOP <see cref="Spring.Aop.Framework.ProxyFactoryObject"/>
/// with a <see cref="Spring.Transaction.Interceptor.TransactionInterceptor"/>.
/// </p>
/// <p>
/// This class is intended to cover the <i>typical</i> case of declarative
/// transaction demarcation: namely, wrapping a (singleton) target object with a
/// transactional proxy, proxying all the interfaces that the target implements.
/// </p>
/// <p>
/// Internally, a <see cref="Spring.Transaction.Interceptor.TransactionInterceptor"/>
/// instance is used, but the user of this class does not have to care. Optionally, an
/// <see cref="Spring.Aop.IPointcut"/> can be specified to cause conditional invocation of
/// the underlying <see cref="Spring.Transaction.Interceptor.TransactionInterceptor"/>.
/// </p>
/// <p>
/// The
/// <see cref="Spring.Transaction.Interceptor.TransactionProxyFactoryObject.PreInterceptors"/>
/// and
/// <see cref="Spring.Transaction.Interceptor.TransactionProxyFactoryObject.PostInterceptors"/>
/// properties can be set to add additional interceptors to the mix.
/// </p>
/// </remarks>
/// <author>Juergen Hoeller</author>
/// <author>Dmitriy Kopylenko</author>
/// <author>Rod Johnson</author>
/// <author>Griffin Caprio (.NET)</author>
public class TransactionProxyFactoryObject : ProxyConfig, IFactoryObject, IInitializingObject
{
private TransactionInterceptor _transactionInterceptor;
private object _target;
private Type[] _proxyInterfaces;
private TruePointcut _pointcut;
private object[] _preInterceptors;
private object[] _postInterceptors;
private IAdvisorAdapterRegistry _advisorAdapterRegistry = GlobalAdvisorAdapterRegistry.Instance;
private object _proxy;
/// <summary>
/// Creates a new instance of the
/// <see cref="Spring.Transaction.Interceptor.TransactionProxyFactoryObject"/> class.
/// </summary>
public TransactionProxyFactoryObject()
{
_transactionInterceptor = new TransactionInterceptor();
}
/// <summary>
/// Set the transaction manager for this factory.
/// </summary>
/// <remarks>
/// It is this instance that will perform actual transaction management: this class is
/// just a way of invoking it.
/// </remarks>
public IPlatformTransactionManager PlatformTransactionManager
{
set { _transactionInterceptor.TransactionManager = value; }
}
/// <summary>
/// Set the target object, i.e. the object to be wrapped with a transactional proxy.
/// </summary>
/// <remarks>
/// <p>
/// The target may be any object, in which case a
/// <see cref="Spring.Aop.Target.SingletonTargetSource"/> will
/// be created. If it is a <see cref="EmptyTargetSource"/>, no wrapper
/// <see cref="EmptyTargetSource"/> is created: this enables the use of a pooling
/// or prototype <see cref="EmptyTargetSource"/>.
/// </p>
/// </remarks>
public object Target
{
set { _target = value; }
}
/// <summary>
/// Specify the set of interfaces being proxied.
/// </summary>
/// <remarks>
/// <p>
/// If left null (the default), the AOP infrastructure works
/// out which interfaces need proxying by analyzing the target,
/// proxying <b>all</b> of the interfaces that the target object implements.
/// </p>
/// </remarks>
public string[] ProxyInterfaces
{
set { _proxyInterfaces = TypeResolutionUtils.ResolveInterfaceArray( value ); }
}
/// <summary>
/// Set properties with method names as keys and transaction attribute
/// descriptors as values.
/// </summary>
/// <remarks>
/// <p>
/// The various transaction attribute descriptors are parsed via
/// an instance of the
/// <see cref="Spring.Transaction.Interceptor.TransactionAttributeEditor"/> class.
/// </p>
/// <note>
/// Method names are always applied to the target class, no matter if defined in an
/// interface or the class itself.
/// </note>
/// <p>
/// Internally, a
/// <see cref="Spring.Transaction.Interceptor.NameMatchTransactionAttributeSource"/>
/// will be created from the given properties.
/// </p>
/// </remarks>
/// <example>
/// <p>
/// An example string (method name and transaction attributes) might be:
/// </p>
/// <p>
/// key = "myMethod", value = "PROPAGATION_REQUIRED,readOnly".
/// </p>
/// </example>
public NameValueCollection TransactionAttributes
{
set { _transactionInterceptor.TransactionAttributes = value; }
}
/// <summary>
/// Set the transaction attribute source which is used to find transaction
/// attributes.
/// </summary>
/// <remarks>
/// <p>
/// If specifying a <see cref="System.String"/> property value, an
/// appropriate <see cref="System.ComponentModel.TypeConverter"/>
/// implementation will create a
/// <see cref="Spring.Transaction.Interceptor.MethodMapTransactionAttributeSource"/>
/// from the value.
/// </p>
/// </remarks>
public ITransactionAttributeSource TransactionAttributeSource
{
set { _transactionInterceptor.TransactionAttributeSource = value; }
}
/// <summary>
/// Set a pointcut, i.e an object that can cause conditional invocation
/// of the <see cref="Spring.Transaction.Interceptor.TransactionInterceptor"/>
/// depending on method and attributes passed.
/// </summary>
/// <remarks>
/// <note>
/// Additional interceptors are always invoked.
/// </note>
/// </remarks>
public TruePointcut TruePointcut
{
set { _pointcut = value; }
}
/// <summary>
/// Set additional interceptors (or advisors) to be applied before the
/// implicit transaction interceptor.
/// </summary>
public object[] PreInterceptors
{
set { _preInterceptors = value; }
}
/// <summary>
/// Set additional interceptors (or advisors) to be applied after the
/// implicit transaction interceptor.
/// </summary>
/// <remarks>
/// <p>
/// Note that this is just necessary if you rely on those interceptors in general.
/// </p>
/// </remarks>
public object[] PostInterceptors
{
set { _postInterceptors = value; }
}
/// <summary>
/// Specify the <see cref="Spring.Aop.Framework.Adapter.IAdvisorAdapterRegistry"/> to use.
/// </summary>
/// <remarks>The default instance is the global AdvisorAdapterRegistry.</remarks>
public IAdvisorAdapterRegistry AdvisorAdapterRegistry
{
set { _advisorAdapterRegistry = value; }
}
#region IFactoryObject Members
/// <summary>
/// Returns the object <see cref="System.Type"/> for this proxy factory.
/// </summary>
public Type ObjectType
{
get
{
if ( _proxy != null )
{
return _proxy.GetType();
}
else if ( _target != null && _target is ITargetSource )
{
return _target.GetType();
}
else
{
return null;
}
}
}
/// <summary>
/// Returns the object wrapped by this proxy factory.
/// </summary>
/// <returns>The target object proxy.</returns>
public object GetObject()
{
return _proxy;
}
/// <summary>
/// Is this object a singleton? Always returns <b>true</b> in this implementation.
/// </summary>
public bool IsSingleton
{
get
{
return true;
}
}
#endregion
#region IInitializingObject Members
/// <summary>
/// Method run after all the properties have been set for this object.
/// Responsible for actual proxy creation.
/// </summary>
public void AfterPropertiesSet()
{
_transactionInterceptor.AfterPropertiesSet();
if ( _target == null )
{
throw new ArgumentException("'target' is required.");
}
ProxyFactory proxyFactory = new ProxyFactory();
if ( _preInterceptors != null )
{
for ( int i = 0; i < _preInterceptors.Length; i++ )
{
proxyFactory.AddAdvisor(_advisorAdapterRegistry.Wrap(_preInterceptors[i]));
}
}
if ( _pointcut != null )
{
IAdvisor advice = new DefaultPointcutAdvisor(_pointcut, _transactionInterceptor);
proxyFactory.AddAdvisor(advice);
}
else
{
proxyFactory.AddAdvisor( new TransactionAttributeSourceAdvisor( _transactionInterceptor ) );
}
if ( _postInterceptors != null )
{
for ( int i = 0; i < _postInterceptors.Length; i++ )
{
proxyFactory.AddAdvisor(_advisorAdapterRegistry.Wrap(_postInterceptors[i]));
}
}
proxyFactory.CopyFrom(this);
proxyFactory.TargetSource = createTargetSource(_target);
if ( _proxyInterfaces != null )
{
proxyFactory.Interfaces = _proxyInterfaces;
}
else if ( !ProxyTargetType )
{
if ( _target is ITargetSource )
{
throw new AopConfigException("Either 'ProxyInterfaces' or 'ProxyTargetType' is required " +
"when using an ITargetSource as 'target'");
}
proxyFactory.Interfaces = AopUtils.GetAllInterfaces(_target);
}
_proxy = proxyFactory.GetProxy();
}
#endregion
/// <summary>
/// Set the target or <see cref="Spring.Aop.ITargetSource"/>.
/// </summary>
/// <param name="target">
/// The target. If this is an implementation of the <see cref="Spring.Aop.ITargetSource"/>
/// interface, it is used as our <see cref="Spring.Aop.ITargetSource"/>; otherwise it is
/// wrapped in a <see cref="Spring.Aop.Target.SingletonTargetSource"/>.
/// </param>
/// <returns>An <see cref="Spring.Aop.ITargetSource"/> for this object.</returns>
protected ITargetSource createTargetSource( object target )
{
if ( target is ITargetSource )
{
return (ITargetSource) target;
} else
{
return new SingletonTargetSource( target );
}
}
}
}
|