From: Michael D. <mik...@us...> - 2004-09-27 03:42:20
|
Update of /cvsroot/nhibernate/nhibernate/src/NHibernate/Proxy In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv30054/src/NHibernate/Proxy Modified Files: Tag: alpha_avalon-proxy LazyInitializer.cs Added Files: Tag: alpha_avalon-proxy AvalonLazyInitializer.cs AvalonProxyGenerator.cs INHibernateProxy.cs IProxyGenerator.cs NHibernateProxyHelper.cs ProxyGeneratorFactory.cs SerializableProxy.cs Removed Files: Tag: alpha_avalon-proxy HibernateProxy.cs HibernateProxyHelper.cs IHibernateProxy.cs Log Message: commit on the avalon-proxy branch of a semi-working (no serialization) of proxies yet. --- NEW FILE: SerializableProxy.cs --- using System; using System.Reflection; using System.Runtime.Serialization; namespace NHibernate.Proxy { /// <summary> /// Serializable "container" for a NHibernate Proxy. /// </summary> /// <remarks> /// <para> /// A NHibernate Proxy is not actually Serialized from Server to Client because /// it is a dynamically generated Type that does not exist in the Client AppDomain. /// </para> /// <para> /// This Class holds all of the information needed to rebuild the NHibernate Proxy /// during Deserialization. /// </para> /// </remarks> [Serializable] public class SerializableProxy : IObjectReference { private System.Type _persistentClass; private System.Type[] _interfaces; private object _id; private PropertyInfo _identifierPropertyInfo; /// <summary> /// Construct a container that can recreate the Proxy after Serialization. /// </summary> /// <param name="persistentClass">The PersistentClass to rebuild the Proxy for.</param> /// <param name="interfaces">The Interfaces for the Proxy.</param> /// <param name="id">The value of the id for the Proxy.</param> /// <param name="identifierPropertyInfo">The Identifier Property.</param> public SerializableProxy(System.Type persistentClass, System.Type[] interfaces, object id, PropertyInfo identifierPropertyInfo) { _persistentClass = persistentClass; _interfaces = interfaces; _id = id; _identifierPropertyInfo = identifierPropertyInfo; // don't need to do anything fancy here - in java a java.lang.reflect.Method is not serializable // so it had to be converted into a Class and Method Name to rebuild the Method object, but in // .NET the PropertyInfo is marked as serializable... } #region IObjectReference Members /// <summary> /// Converts a Serializable Proxy into the dynamically generated Proxy on the Client side. /// </summary> /// <param name="context"></param> /// <returns></returns> /// <remarks> /// This is called after the Deserialization ctor of this Class has been called. /// </remarks> public object GetRealObject(StreamingContext context) { IProxyGenerator generator = ProxyGeneratorFactory.GetProxyGenerator(); object proxy = generator.GetProxy(_persistentClass, _interfaces, _identifierPropertyInfo, _id, null); return proxy; } #endregion } } --- HibernateProxyHelper.cs DELETED --- --- NEW FILE: NHibernateProxyHelper.cs --- using System; namespace NHibernate.Proxy { /// <summary> /// NHibernateProxyHelper provides convenience methods for working with /// objects that might be instances of Classes or the Proxied version of /// the Class. /// </summary> public sealed class NHibernateProxyHelper { private NHibernateProxyHelper() { //can't instantiate } /// <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) { // have to hard code in "handler" - very dependant on them not changing their // implementation - email Hammet about this - or atleast to provide a static // field object fieldValue = proxy.GetType().GetField( "handler" ).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 static System.Type GetClass(object obj) { if (obj is INHibernateProxy) { INHibernateProxy proxy = (INHibernateProxy) obj; LazyInitializer li = GetLazyInitializer(proxy); return li.PersistentClass; } else { return obj.GetType(); } } } } --- NEW FILE: AvalonProxyGenerator.cs --- using System; using System.Collections; using System.Reflection; using Apache.Avalon.DynamicProxy; using NHibernate.Engine; namespace NHibernate.Proxy { /// <summary> /// An <see cref="IProxyGenerator"/> for the Apache.Avalon.DynamicProxy library. /// </summary> public class AvalonProxyGenerator : IProxyGenerator { private static readonly log4net.ILog log = log4net.LogManager.GetLogger( typeof(AvalonProxyGenerator) ); private ProxyGenerator _generator = new ProxyGenerator(); /// <summary> /// Initializes a new instance of the <see cref="AvalonProxyGenerator"/> class. /// </summary> internal AvalonProxyGenerator() { } #region IProxyGenerator Methods /// <summary> /// Build a proxy using the Apache.Avalon.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[] interfaces, PropertyInfo identifierPropertyInfo, object id, ISessionImplementor session) { try { AvalonLazyInitializer initializer = new AvalonLazyInitializer( persistentClass, interfaces, id, identifierPropertyInfo, session ); // 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( persistentClass.IsInterface ) { System.Type[] temp = new System.Type[ interfaces.Length + 1 ]; interfaces.CopyTo( temp, 0 ); temp[ interfaces.Length ] = persistentClass; interfaces = temp; } object generatedProxy = _generator.CreateProxy( interfaces, initializer ); return (INHibernateProxy)generatedProxy; } catch(Exception e) { log.Error("Avalon Dynamic Class Generator failed", e); throw new HibernateException( "Avalon Dynamic Class Generator failed", e); } } #endregion } } --- IHibernateProxy.cs DELETED --- --- NEW FILE: IProxyGenerator.cs --- using System; using System.Reflection; using NHibernate.Engine; namespace NHibernate.Proxy { /// <summary> /// Summary description for IProxyGenerator. /// </summary> public interface IProxyGenerator { /// <summary> /// Build a proxy using the Apache.Avalon.DynamicProxy library. /// </summary> /// <param name="persistentClass">The PersistentClass to proxy.</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> INHibernateProxy GetProxy(System.Type persistentClass, System.Type[] interfaces, PropertyInfo identifierPropertyInfo, object id, ISessionImplementor session); } } --- NEW FILE: INHibernateProxy.cs --- using System; using System.Runtime.Serialization; namespace NHibernate.Proxy { /// <summary> /// A marker interface so NHibernate can know if it is dealing with /// an object that is a Proxy. All NHibernate Proxies will be Serializable. /// This does not mean that all Mapped Classes have to be Serializable. /// </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 : ISerializable { } } Index: LazyInitializer.cs =================================================================== RCS file: /cvsroot/nhibernate/nhibernate/src/NHibernate/Proxy/LazyInitializer.cs,v retrieving revision 1.1 retrieving revision 1.1.2.1 diff -C2 -d -r1.1 -r1.1.2.1 *** LazyInitializer.cs 22 Apr 2003 14:07:21 -0000 1.1 --- LazyInitializer.cs 27 Sep 2004 03:42:03 -0000 1.1.2.1 *************** *** 1,3 **** --- 1,4 ---- using System; + using System.Collections; using System.Reflection; *************** *** 5,169 **** using NHibernate.Util; - using log4net; - namespace NHibernate.Proxy { /// <summary> ! /// Summary description for LazyInitializer. /// </summary> ! public abstract class LazyInitializer { ! protected static readonly object INVOKE_IMPLEMENTATION = new object(); ! protected object target = null; ! protected object id; ! protected ISessionImplementor session; ! protected System.Type persistentClass; ! protected MethodInfo getIdentifierMethod; ! protected bool overridesEquals; ! public LazyInitializer(System.Type persistentClass, object id, MethodInfo getIdentifierMethod, ISessionImplementor session) { ! this.id = id; ! this.session = session; ! this.persistentClass = persistentClass; ! this.getIdentifierMethod = getIdentifierMethod; ! overridesEquals = ReflectHelper.OverridesEquals(persistentClass); } ! ! public void Initialize() { ! if (target==null) { ! if ( session==null ) { ! throw new HibernateException("Could not initialize proxy - no Session"); } ! else if ( !session.IsOpen ) { ! throw new HibernateException("Could not initialize proxy - the owning Session was closed"); } ! else { ! target = session.ImmediateLoad(persistentClass, id); } } } ! private void InitializeWrapExceptions() { ! try { Initialize(); } ! catch (Exception e) { ! LogManager.GetLogger( typeof(LazyInitializer) ).Error("Exception initializing proxy", e); throw new LazyInitializationException(e); } } ! protected abstract object SerializableProxy(); ! protected object Invoke(MethodInfo method, object[] args) { ! ! string methodName = method.Name; ! int pars = method.GetParameters().Length; ! if ( pars==0 ) { ! if ( "WriteReplace".Equals(methodName) ) { ! if (target==null && session!=null ) target = session.GetEntity( ! new Key( id, session.Factory.GetPersister(persistentClass) ) ! ); ! if (target==null) { ! /*if ( session==null || !session.isOpen() ) { ! return session.getFactory().getPersister(persistentClass).instantiate(id); //A little "empty" object ! } ! else {*/ ! return SerializableProxy(); ! //} } ! else { ! return target; } - } ! else if ( !overridesEquals && getIdentifierMethod!=null && "GetHashCode".Equals(methodName) ) { ! // kinda dodgy, since it redefines the hashcode of the proxied object. ! // but necessary if we are to keep proxies in HashSets without ! // forcing them to be initialized ! return id.GetHashCode(); } ! else if ( method.Equals(getIdentifierMethod) ) { ! return id; } ! else if ( "Finalize".Equals( methodName ) ) { ! return null; } - } ! else if ( pars==1 && !overridesEquals && getIdentifierMethod!=null && "Equals".Equals(methodName) ) { ! // less dodgy because Hibernate forces == to be same as identifier equals ! return (bool) id.Equals( getIdentifierMethod.Invoke( args[0], null ) ); } ! // otherwise: ! return INVOKE_IMPLEMENTATION; ! ! /*try { ! return method.invoke( getImplementation(), args ); } - catch (InvocationTargetException ite) { - throw ite.getTargetException(); - }*/ } ! public object Identifier { ! get { ! return id; } ! } ! ! public System.Type PersistentClass { ! get { ! return persistentClass; } ! } ! ! public bool IsUninitialized { ! get { ! return target == null; } ! } ! ! public ISessionImplementor Session { ! get { ! return session; } ! } ! ! public void SetSession(ISessionImplementor s) { ! if (s!=session) { ! if ( session!=null && session.IsOpen ) { ! //TODO: perhaps this should be some other RuntimeException... ! throw new LazyInitializationException("Illegally attempted to associate a proxy with two open Sessions"); } ! else { ! session = s; } } } - /// <summary> - /// Return the underlying persistent object, initializing if necessary - /// </summary> - public object GetImplementation() { - InitializeWrapExceptions(); - return target; - } - - /// <summary> - /// Return the underlying persistent object in the given Session, or null - /// </summary> - /// <param name="s"></param> - /// <returns></returns> - public object GetImplementation(ISessionImplementor s) { - return s.GetEntity( new Key( - Identifier, - s.Factory.GetPersister( PersistentClass ) - ) ); - } } ! } \ No newline at end of file --- 6,297 ---- using NHibernate.Util; namespace NHibernate.Proxy { /// <summary> ! /// Provides the base functionallity to Handle Member calls into a dynamically ! /// generated NHibernate Proxy. /// </summary> ! /// <remarks> ! /// This could be an extension point later if the .net framework ever gets a Proxy ! /// class that is similar to the java.lang.reflect.Proxy or if a library similar ! /// to cglib was made in .net. ! /// </remarks> ! public abstract class LazyInitializer ! { ! /// <summary> ! /// If this is returned by Invoke then the subclass needs to Invoke the ! /// method call against the object that is being proxied. ! /// </summary> ! protected static readonly object InvokeImplementation = new object(); ! protected object _target = null; ! protected object _id; ! protected ISessionImplementor _session; ! protected System.Type _persistentClass; ! protected PropertyInfo _identifierPropertyInfo; ! protected bool _overridesEquals; ! private SerializableProxy _serializableProxy; ! ! /// <summary> ! /// Create a LazyInitializer to handle all of the Methods/Properties that are called ! /// on the Proxy. ! /// </summary> ! /// <param name="persistentClass">The Class to Proxy.</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> ! protected LazyInitializer(System.Type persistentClass, object id, PropertyInfo identifierPropertyInfo, ISessionImplementor session) ! { ! _persistentClass = persistentClass; ! _id = id; ! _session = session; ! _identifierPropertyInfo = identifierPropertyInfo; ! _overridesEquals = ReflectHelper.OverridesEquals(_persistentClass); } ! ! /// <summary> ! /// Perform an ImmediateLoad of the actual object for the Proxy. ! /// </summary> ! /// <exception cref="HibernateException"> ! /// Thrown when the Proxy has no Session or the Session is not open. ! /// </exception> ! public void Initialize() ! { ! if( _target==null ) ! { ! if( _session==null ) ! { ! throw new HibernateException( "Could not initialize proxy - no Session." ); } ! else if( _session.IsOpen==false ) ! { ! throw new HibernateException( "Could not initialize proxy - the owning Session was closed." ); } ! else ! { ! _target = _session.ImmediateLoad( _persistentClass, _id ); } } } ! /// <summary> ! /// Initializes the Proxy. ! /// </summary> ! /// <remarks> ! /// If an Exception is thrown then it will be logged and wrapped into a ! /// <see cref="LazyInitializationException" />. ! /// </remarks> ! /// <exception cref="LazyInitializationException"> ! /// Thrown whenever a problem is encountered during the Initialization of the Proxy. ! /// </exception> ! private void InitializeWrapExceptions() ! { ! try ! { Initialize(); } ! catch( Exception e ) ! { ! log4net.LogManager.GetLogger(typeof(LazyInitializer)).Error("Exception initializing proxy.", e); throw new LazyInitializationException(e); } } ! /// <summary> ! /// Converts this Proxy into a Serializable Class that knows how to create a ! /// Proxy during the Deserialization process. ! /// </summary> ! /// <returns>SerializableProxy that can Deserialize this LazyInitializer.</returns> ! protected abstract SerializableProxy SerializableProxy() ; ! public object Identifier ! { ! get { return _id; } ! } ! public System.Type PersistentClass ! { ! get { return _persistentClass; } ! } ! public bool IsUninitialized ! { ! get { return (_target==null); } ! } ! public ISessionImplementor Session ! { ! get { return _session; } ! set ! { ! if(value!=_session) ! { ! if( _session!=null && _session.IsOpen ) ! { ! //TODO: perhaps this should be some other RuntimeException... ! throw new LazyInitializationException("Illegally attempted to associate a proxy with two open Sessions"); } ! else ! { ! _session = value; } } ! } ! } ! ! /// <summary> ! /// Return the Underlying Persistent Object, initializing if necessary. ! /// </summary> ! /// <returns>The Persistent Object this proxy is Proxying.</returns> ! public object GetImplementation() ! { ! InitializeWrapExceptions(); ! return _target; ! } ! ! /// <summary> ! /// Return the Underlying Persistent Object in a given <see cref="ISession"/>, or null. ! /// </summary> ! /// <param name="s">The Session to get the object from.</param> ! /// <returns>The Persistent Object this proxy is Proxying, or <c>null</c>.</returns> ! public object GetImplementation(ISessionImplementor s) ! { ! Key key = new Key( Identifier, s.Factory.GetPersister(PersistentClass) ); ! return s.GetEntity(key); ! } ! ! /// <summary> ! /// Invokes the method if this is something that the LazyInitializer can handle ! /// 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> ! /// The result of the Invoke if the underlying proxied object is not needed. If the ! /// underlying proxied object is needed then it returns the result <see cref="InvokeImplementation"/> ! /// which indicates that the Proxy will need to forward to the real implementation. ! /// </returns> ! public virtual object Invoke(MethodInfo method, params object[] args) ! { ! // all Proxies must implement INHibernateProxy which extends ISerializable ! if( method.Name.Equals("GetObjectData") ) ! { ! if( _target==null & _session!=null ) ! { ! Key key = new Key(_id, _session.Factory.GetPersister( _persistentClass ) ); ! _target = _session.GetEntity( key ); } ! ! if( _target==null ) ! { ! if( _serializableProxy==null ) ! { ! _serializableProxy = SerializableProxy(); ! } ! return _serializableProxy; } ! else ! { ! return _target; } } ! else if( !_overridesEquals && _identifierPropertyInfo!=null && method.Name.Equals("GetHashCode") ) ! { ! // kinda dodgy, since it redefines the hashcode of the proxied object. ! // but necessary if we are to keep proxies in HashSets without ! // forcing them to be initialized ! return _id.GetHashCode(); ! } ! else if( _identifierPropertyInfo!=null && method.Equals( _identifierPropertyInfo.GetGetMethod(true) ) ) ! // .Name.Equals( "get_" + _identifierPropertyInfo.Name ) ) -> this was removed because of null _identifierPropertyInfo problems ! { ! return _id; ! } ! else if( method.Name.Equals( "Dispose" ) ) ! { ! return null; } ! else if ( args.Length==1 && !_overridesEquals && _identifierPropertyInfo!=null && method.Name.Equals( "Equals" ) ) ! { ! // less dodgy because NHibernate forces == to be the same as Identifier Equals ! return _id.Equals( _identifierPropertyInfo.GetValue( _target, null ) ); ! } ! ! else ! { ! return InvokeImplementation; } } ! #region Helper Class for all of the Parameters to InvokeMember ! ! protected class InvokeMemberParams ! { ! private string _methodName; ! private BindingFlags _invokeAttr; ! private static IDictionary _knownPrefixes; ! ! static InvokeMemberParams () ! { ! BindingFlags defaultFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; ! _knownPrefixes = new Hashtable(6); ! _knownPrefixes.Add("", defaultFlags | BindingFlags.InvokeMethod); ! _knownPrefixes.Add("get", defaultFlags | BindingFlags.GetProperty); ! _knownPrefixes.Add("set", defaultFlags | BindingFlags.SetProperty); } ! ! private InvokeMemberParams(string methodName, BindingFlags invokeAttr) ! { ! _methodName = methodName; ! _invokeAttr = invokeAttr; } ! ! public BindingFlags InvokeAttr ! { ! get { return _invokeAttr; } } ! ! public string MethodName ! { ! get { return _methodName; } } ! ! public static InvokeMemberParams GetInvokeMemberParams(string methodName) ! { ! int indexOfFirstUnderscore = methodName.IndexOf("_"); ! string prefix = String.Empty; ! ! string method; ! BindingFlags flags; ! ! // there was no underscore found in the name - has to be a method that ! // is being called. ! if(indexOfFirstUnderscore==-1) ! { ! return new InvokeMemberParams(methodName, (BindingFlags)_knownPrefixes[""] ); } ! ! prefix = methodName.Substring(0, indexOfFirstUnderscore); ! ! // there was an underscore found - is this a property get_/set_ or just a bad naming ! // convention that someone is following ! if(_knownPrefixes.Contains(prefix)) ! { ! method = methodName.Substring(indexOfFirstUnderscore + 1, methodName.Length - (indexOfFirstUnderscore + 1) ); ! flags = (BindingFlags)_knownPrefixes[prefix]; ! ! return new InvokeMemberParams(method, flags); ! } ! else ! { ! // bad naming convention was used - an "_" in a method name??? ! return new InvokeMemberParams(methodName, (BindingFlags)_knownPrefixes[""] ); } } } + #endregion } ! } --- NEW FILE: AvalonLazyInitializer.cs --- using System; using System.Collections; using System.Reflection; using Apache.Avalon.DynamicProxy; using NHibernate.Engine; namespace NHibernate.Proxy { /// <summary> /// A <see cref="NLazyInitiliazer"/> built using Avalon's Dynamic Class Generator. /// </summary> public class AvalonLazyInitializer : LazyInitializer, IInvocationHandler { private static readonly log4net.ILog log = log4net.LogManager.GetLogger( typeof(AvalonLazyInitializer) ); private System.Type[] _interfaces; /// <summary> /// /// </summary> /// <param name="persistentClass"></param> /// <param name="interfaces"></param> /// <param name="id"></param> /// <param name="identifierProperty"></param> /// <param name="session"></param> internal AvalonLazyInitializer(System.Type persistentClass, System.Type[] interfaces, object id, PropertyInfo identifierPropertyInfo, ISessionImplementor session) : base (persistentClass, id, identifierPropertyInfo, session) { _interfaces = interfaces; } protected override SerializableProxy SerializableProxy() { return new SerializableProxy( _persistentClass, _interfaces, _id, _identifierPropertyInfo ); } #region Apache.Avalon.DynamicProxy.IInvocationHandler 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="methodName">The name of the Method/Property to Invoke.</param> /// <param name="args">The parameters for the Method/Property</param> /// <returns>The result just like the actual object was called.</returns> public object Invoke(object proxy, MethodInfo method, params object[] arguments) { object result = base.Invoke( method, arguments ); // the base NLazyInitializer could not handle it so we need to Invoke // the method/property against the real class. if(result==InvokeImplementation) { InvokeMemberParams invokeParams = InvokeMemberParams.GetInvokeMemberParams( method.Name ); return method.Invoke( GetImplementation(), arguments ); } else { return result; } } #endregion } } --- NEW FILE: ProxyGeneratorFactory.cs --- using System; using System.Reflection; using Apache.Avalon.DynamicProxy; using NHibernate.Engine; namespace NHibernate.Proxy { /// <summary> /// A Factory for getting the ProxyGenerator. /// </summary> public sealed class ProxyGeneratorFactory { private static readonly log4net.ILog log = log4net.LogManager.GetLogger( typeof(ProxyGeneratorFactory) ); private static AvalonProxyGenerator _generator = new AvalonProxyGenerator(); public static IProxyGenerator GetProxyGenerator() { return _generator; } } } --- HibernateProxy.cs DELETED --- |