From: Michael D. <mik...@us...> - 2004-12-16 21:51:57
|
Update of /cvsroot/nhibernate/nhibernate/src/NHibernate/Proxy In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv16936/NHibernate/Proxy Modified Files: INHibernateProxy.cs LazyInitializer.cs NHibernateProxyHelper.cs ProxyGeneratorFactory.cs Added Files: CastleCustomProxyGenerator.cs CastleLazyInitializer.cs CastleProxyGenerator.cs Log Message: Implementation and Test of proxies using Castle.DynamicProxy. Index: NHibernateProxyHelper.cs =================================================================== RCS file: /cvsroot/nhibernate/nhibernate/src/NHibernate/Proxy/NHibernateProxyHelper.cs,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** NHibernateProxyHelper.cs 9 Dec 2004 17:52:16 -0000 1.2 --- NHibernateProxyHelper.cs 16 Dec 2004 21:51:48 -0000 1.3 *************** *** 16,23 **** /// <summary> ! /// Gets the NLazyInitializer that is used by the Proxy. /// </summary> /// <param name="proxy">The Proxy object</param> ! /// <returns>A reference to NLazyInitializer that contains the details of the Proxied object.</returns> public static LazyInitializer GetLazyInitializer(INHibernateProxy proxy) { --- 16,26 ---- /// <summary> ! /// Gets the <see cref="LazyInitializer"/> that is used by the Proxy. /// </summary> /// <param name="proxy">The Proxy object</param> ! /// <returns> ! /// A reference to <see cref="LazyInitializer"/> that contains the details ! /// of the Proxied object. ! /// </returns> public static LazyInitializer GetLazyInitializer(INHibernateProxy proxy) { --- NEW FILE: CastleProxyGenerator.cs --- using System; using System.Collections; using System.Reflection; using System.Reflection.Emit; using Castle.DynamicProxy; using Castle.DynamicProxy.Builder; using Castle.DynamicProxy.Builder.CodeGenerators; using NHibernate.Engine; namespace NHibernate.Proxy { /// <summary> /// An <see cref="IProxyGenerator"/> that uses the Castle.DynamicProxy library. /// </summary> public class CastleProxyGenerator : IProxyGenerator { private static readonly log4net.ILog log = log4net.LogManager.GetLogger( typeof(CastleProxyGenerator) ); // key = mapped type // value = proxy type private IDictionary cachedProxyTypes; private GeneratorContext _context; private ModuleScope _scope; /// <summary> /// Initializes a new instance of the <see cref="CastleProxyGenerator"/> class. /// </summary> internal CastleProxyGenerator() { cachedProxyTypes = new Hashtable(); _scope = new ModuleScope(); // the EnhanceTypeDelegate will add custom code gen that DynamicProxy does not provide // by default. _context = new GeneratorContext( );// new EnhanceTypeDelegate( EnhanceInterfaceType ), null ); } #region IProxyGenerator Methods /// <summary> /// Build a proxy using the Castle.DynamicProxy library. /// </summary> /// <param name="persistentClass">.</param> /// <param name="interfaces">The extra interfaces the Proxy should implement.</param> /// <param name="identifierPropertyInfo">The PropertyInfo to get/set the Id.</param> /// <param name="id">The value for the Id.</param> /// <param name="session">The Session the proxy is in.</param> /// <returns>A fully built <c>INHibernateProxy</c>.</returns> public INHibernateProxy GetProxy(System.Type persistentClass, System.Type concreteProxy, System.Type[] interfaces, PropertyInfo identifierPropertyInfo, object id, ISessionImplementor session) { object generatedProxy = null; try { CastleLazyInitializer initializer = new CastleLazyInitializer( persistentClass, concreteProxy, interfaces, id, identifierPropertyInfo, session ); System.Type proxyType = null; // if I try to generate a proxy twice for the same type the Castle library will do the same // in one ModuleBuilder. The ModuleBuilder throws an exception (not suprisingly) when you try // to define the same type twice in it. So nh needs to keep a cache of the proxy types that have // already been generated. lock( cachedProxyTypes.SyncRoot ) { proxyType = cachedProxyTypes[ concreteProxy ] as System.Type; // if the pc is an interface then we need to add the interface to the // interfaces array that was passed in because it only includes the extra // interfaces for that persistent class. if( concreteProxy.IsInterface ) { if( proxyType==null ) { //TODO: figure out if this is necessary because the concreteProxy is // already included in the interfaces array... System.Type[] temp = new System.Type[ interfaces.Length + 1 ]; interfaces.CopyTo( temp, 0 ); temp[ interfaces.Length ] = concreteProxy; //persistentClass; interfaces = temp; InterfaceProxyGenerator _interfaceGenerator = new InterfaceProxyGenerator( _scope, _context ); proxyType = _interfaceGenerator.GenerateCode( interfaces ); cachedProxyTypes[ concreteProxy ] = proxyType; } // don't understand why a Interface proxy requires 2 arg constructor and a Class Interface // proxy only requires 1 - TODO: email Hammett about this. // hack with new object() because an interface proxy is expecting an actual target instance - we // don't have that yet and don't want to create it until it is actually needed. generatedProxy = Activator.CreateInstance( proxyType, new object[] { initializer, new object() } ); } else { if( proxyType==null ) { CastleCustomProxyGenerator _classGenerator = new CastleCustomProxyGenerator( _scope, _context ); proxyType = _classGenerator.GenerateCode( concreteProxy ); cachedProxyTypes[ concreteProxy ] = proxyType; } generatedProxy = Activator.CreateInstance( proxyType, new object[] { initializer } ); } } return (INHibernateProxy)generatedProxy; } catch(Exception e) { log.Error("Castle Dynamic Class Generator failed", e); throw new HibernateException( "Castle Dynamic Class Generator failed", e); } } /// <summary> /// Gets the <see cref="LazyInitializer"/> that is used by the Proxy. /// </summary> /// <param name="proxy">The Proxy object</param> /// <returns>The <see cref="LazyInitializer"/> that contains the details of the Proxied object.</returns> public LazyInitializer GetLazyInitializer(INHibernateProxy proxy) { // have to hard code in "__interceptor" - very dependant on them not changing their // implementation - TODO: email Hammet about this - or atleast to provide a static // field object fieldValue = proxy.GetType().GetField( "__interceptor" ).GetValue( proxy ); return (LazyInitializer)fieldValue; } /// <summary> /// Convenience method to figure out the underlying type for the object regardless of it /// is a Proxied object or the real object. /// </summary> /// <param name="obj">The object to get the type of.</param> /// <returns>The Underlying Type for the object regardless of if it is a Proxy.</returns> public System.Type GetClass(object obj) { if (obj is INHibernateProxy) { INHibernateProxy proxy = (INHibernateProxy) obj; LazyInitializer li = GetLazyInitializer( proxy ); return li.PersistentClass; } else { return obj.GetType(); } } #endregion } } Index: INHibernateProxy.cs =================================================================== RCS file: /cvsroot/nhibernate/nhibernate/src/NHibernate/Proxy/INHibernateProxy.cs,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** INHibernateProxy.cs 9 Dec 2004 17:52:16 -0000 1.2 --- INHibernateProxy.cs 16 Dec 2004 21:51:48 -0000 1.3 *************** *** 1,4 **** using System; - using System.Runtime.Serialization; namespace NHibernate.Proxy --- 1,3 ---- *************** *** 9,15 **** /// </summary> /// <remarks> /// This interface should not be implemented by anything other than ! /// the Dynamically generated Proxy. It has to be public scope because /// the Proxies are created in a seperate DLL than NHibernate. /// </remarks> public interface INHibernateProxy --- 8,20 ---- /// </summary> /// <remarks> + /// <para> /// This interface should not be implemented by anything other than ! /// the Dynamically generated Proxy. If it is implemented by a class then ! /// NHibernate will think that class is a Proxy and will not work. ! /// </para> ! /// <para> ! /// It has to be public scope because /// the Proxies are created in a seperate DLL than NHibernate. + /// </para> /// </remarks> public interface INHibernateProxy --- NEW FILE: CastleCustomProxyGenerator.cs --- using System; using Castle.DynamicProxy; using Castle.DynamicProxy.Builder.CodeBuilder; using Castle.DynamicProxy.Builder.CodeGenerators; namespace NHibernate.Proxy { /// <summary> /// This will build a proxy for a <see cref="System.Type"/> that is /// a <c>Class</c> and then add the <see cref="INHibernateProxy"/> /// interface to it. /// </summary> [CLSCompliant(false)] public class CastleCustomProxyGenerator : ClassProxyGenerator { public CastleCustomProxyGenerator(ModuleScope scope) : base(scope) { } public CastleCustomProxyGenerator(ModuleScope scope, GeneratorContext context) : base(scope, context) { } /// <summary> /// Override of the <see cref="BaseCodeGenerator.CreateTypeBuilder(System.Type, System.Type[])"/> implementation. /// </summary> /// <param name="baseType">The base <see cref="System.Type"/> for the Proxy.</param> /// <param name="interfaces">The extra <see cref="System.Type"/> interfaces for the Proxy to implement.</param> /// <returns>An <see cref="EasyType"/> with the required interfaces added.</returns> protected override EasyType CreateTypeBuilder(System.Type baseType, System.Type[] interfaces) { bool isImplemented = false; // check to see if this implements INHibernateProxy - if not then make it // implement the interface for( int i=0; i<interfaces.Length; i++) { if( interfaces[i].Equals( typeof(INHibernateProxy) ) ) { isImplemented = true; } } if( isImplemented==false ) { int length = interfaces.Length; System.Type[] newInterfaces = new System.Type[ length + 1 ]; Array.Copy( interfaces, 0, newInterfaces, 0, length ); newInterfaces[ length ] = typeof(INHibernateProxy); interfaces = newInterfaces; } return base.CreateTypeBuilder(baseType, interfaces); } } } Index: LazyInitializer.cs =================================================================== RCS file: /cvsroot/nhibernate/nhibernate/src/NHibernate/Proxy/LazyInitializer.cs,v retrieving revision 1.3 retrieving revision 1.4 diff -C2 -d -r1.3 -r1.4 *** LazyInitializer.cs 9 Dec 2004 17:52:16 -0000 1.3 --- LazyInitializer.cs 16 Dec 2004 21:51:48 -0000 1.4 *************** *** 106,109 **** --- 106,113 ---- /// </summary> /// <param name="info">The <see cref="SerializationInfo"/> to write the object to.</param> + /// <remarks> + /// This will only be called if the Dynamic Proxy generator does not handle serialization + /// itself or delegates calls to the method GetObjectData to the LazyInitializer. + /// </remarks> protected virtual void AddSerializationInfo(SerializationInfo info) { *************** *** 170,174 **** /// without the underlying proxied object being instantiated. /// </summary> ! /// <param name="methodName">The name of the method/property to Invoke.</param> /// <param name="args">The arguments to pass the method/property.</param> /// <returns> --- 174,178 ---- /// without the underlying proxied object being instantiated. /// </summary> ! /// <param name="method">The name of the method/property to Invoke.</param> /// <param name="args">The arguments to pass the method/property.</param> /// <returns> *************** *** 179,186 **** public virtual object Invoke(MethodBase method, params object[] args) { ! // all Proxies must implement INHibernateProxy which extends ISerializable ! // not true anymore - DynamicProxy now handles the serialization. All Proxy ! // Generators should make sure that ISerializable is implemented if their ! // Proxy doesn't do it by default. if( method.Name.Equals("GetObjectData") ) { --- 183,189 ---- public virtual object Invoke(MethodBase method, params object[] args) { ! // if the Proxy Engine delegates the call of GetObjectData to the Initializer ! // then we need to handle it. Castle.DynamicProxy takes care of serializing ! // proxies for us, but other providers might not. if( method.Name.Equals("GetObjectData") ) { Index: ProxyGeneratorFactory.cs =================================================================== RCS file: /cvsroot/nhibernate/nhibernate/src/NHibernate/Proxy/ProxyGeneratorFactory.cs,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** ProxyGeneratorFactory.cs 9 Dec 2004 17:52:16 -0000 1.2 --- ProxyGeneratorFactory.cs 16 Dec 2004 21:51:48 -0000 1.3 *************** *** 1,9 **** using System; - using System.Reflection; - - //TODO: build a version - //using Castle.DynamicProxy; - - using NHibernate.Engine; namespace NHibernate.Proxy --- 1,3 ---- *************** *** 16,21 **** private static readonly log4net.ILog log = log4net.LogManager.GetLogger( typeof(ProxyGeneratorFactory) ); ! //TODO: build a version ! private static IProxyGenerator _generator = null;// new CastleProxyGenerator(); public static IProxyGenerator GetProxyGenerator() --- 10,14 ---- private static readonly log4net.ILog log = log4net.LogManager.GetLogger( typeof(ProxyGeneratorFactory) ); ! private static IProxyGenerator _generator = new CastleProxyGenerator(); public static IProxyGenerator GetProxyGenerator() --- NEW FILE: CastleLazyInitializer.cs --- using System; using System.Reflection; using Castle.DynamicProxy; using NHibernate.Engine; namespace NHibernate.Proxy { /// <summary> /// A <see cref="LazyInitializer"/> for use with the Castle Dynamic Class Generator. /// </summary> [Serializable] [CLSCompliant(false)] public class CastleLazyInitializer : LazyInitializer, Castle.DynamicProxy.IInterceptor//, ISerializable { private static readonly log4net.ILog log = log4net.LogManager.GetLogger( typeof(CastleLazyInitializer) ); private System.Type _concreteProxy; private System.Type[] _interfaces; /// <summary> /// Initializes a new <see cref="CastleLazyInitializer"/> object. /// </summary> /// <param name="persistentClass">The Class to Proxy.</param> /// <param name="concreteProxy">The <see cref="System.Type"/> to use as the Proxy.</param> /// <param name="interfaces">An array of <see cref="System.Type"/> interfaces that the Proxy should implement.</param> /// <param name="id">The Id of the Object we are Proxying.</param> /// <param name="identifierPropertyInfo">The PropertyInfo for the <id> property.</param> /// <param name="session">The ISession this Proxy is in.</param> internal CastleLazyInitializer(System.Type persistentClass, System.Type concreteProxy, System.Type[] interfaces, object id, PropertyInfo identifierPropertyInfo, ISessionImplementor session) : base (persistentClass, id, identifierPropertyInfo, session) { _concreteProxy = concreteProxy; _interfaces = interfaces; } #region Castle.DynamicProxy.IInterceptor Members /// <summary> /// Invoke the actual Property/Method using the Proxy or instantiate the actual /// object and use it when the Proxy can't handle the method. /// </summary> /// <param name="invocation">The <see cref="IInvocation"/> from the generated Castle.DynamicProxy.</param> /// <param name="args">The parameters for the Method/Property</param> /// <returns>The result just like the actual object was called.</returns> public object Intercept(IInvocation invocation, params object[] args) { // let the generic LazyInitializer figure out if this can be handled // with the proxy or if the real class needs to be initialized object result = base.Invoke( invocation.Method, args); // the base LazyInitializer could not handle it so we need to Invoke // the method/property against the real class if( result==InvokeImplementation ) { invocation.InvocationTarget = GetImplementation(); return invocation.Proceed( args ); } else { return result; } } #endregion } } |