hammett 2004/03/30 19:45:08 Modified: avalon-net/Castle/MicroKernel Apache.Avalon.Castle.MicroKernel.csproj AssemblyInfo.cs AssertUtil.cs BaseKernel.cs ConstructionInfo.cs avalon-net/Castle/MicroKernel/Factory SimpleComponentFactory.cs avalon-net/Castle/MicroKernel/Handlers SimpleHandler.cs avalon-net/Castle/MicroKernel/MicroKernelTest AssemblyInfo.cs MicroKernelTest.csproj SimpleComponentFactoryTestCase.cs Added: avalon-net/Castle/MicroKernel/Handlers AbstractHandler.cs avalon-net/Castle/MicroKernel/MicroKernelTest/Components ICustomerManager.cs IMailMarketingService.cs SimpleCustomerManager.cs SimpleMailMarketingService.cs Log: Added "dependency injection" through set methods (properties) Revision Changes Path 1.2 +5 -0 avalon-sandbox/avalon-net/Castle/MicroKernel/Apache.Avalon.Castle.MicroKernel.csproj Index: Apache.Avalon.Castle.MicroKernel.csproj =================================================================== RCS file: /home/cvs/avalon-sandbox/avalon-net/Castle/MicroKernel/Apache.Avalon.Castle.MicroKernel.csproj,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- Apache.Avalon.Castle.MicroKernel.csproj 31 Mar 2004 00:43:30 -0000 1.1 +++ Apache.Avalon.Castle.MicroKernel.csproj 31 Mar 2004 03:45:08 -0000 1.2 @@ -174,6 +174,11 @@ BuildAction = "Compile" /> <File + RelPath = "Handlers\AbstractHandler.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File RelPath = "Handlers\SimpleHandler.cs" SubType = "Code" BuildAction = "Compile" 1.2 +1 -1 avalon-sandbox/avalon-net/Castle/MicroKernel/AssemblyInfo.cs Index: AssemblyInfo.cs =================================================================== RCS file: /home/cvs/avalon-sandbox/avalon-net/Castle/MicroKernel/AssemblyInfo.cs,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- AssemblyInfo.cs 31 Mar 2004 00:43:30 -0000 1.1 +++ AssemblyInfo.cs 31 Mar 2004 03:45:08 -0000 1.2 @@ -1,4 +1,4 @@ -// Copyright 2004 Apache Software Foundation +// Copyright 2004 The Apache Software Foundation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. 1.2 +3 -1 avalon-sandbox/avalon-net/Castle/MicroKernel/AssertUtil.cs Index: AssertUtil.cs =================================================================== RCS file: /home/cvs/avalon-sandbox/avalon-net/Castle/MicroKernel/AssertUtil.cs,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- AssertUtil.cs 31 Mar 2004 00:43:30 -0000 1.1 +++ AssertUtil.cs 31 Mar 2004 03:45:08 -0000 1.2 @@ -29,7 +29,9 @@ { if (argValue == null) { - throw new ArgumentNullException(argName, String.Format("Argument {0} can't be null", argName) ); + throw new ArgumentNullException( + argName, + String.Format("Argument {0} can't be null", argName) ); } } } 1.2 +2 -0 avalon-sandbox/avalon-net/Castle/MicroKernel/BaseKernel.cs Index: BaseKernel.cs =================================================================== RCS file: /home/cvs/avalon-sandbox/avalon-net/Castle/MicroKernel/BaseKernel.cs,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- BaseKernel.cs 31 Mar 2004 00:43:30 -0000 1.1 +++ BaseKernel.cs 31 Mar 2004 03:45:08 -0000 1.2 @@ -221,6 +221,8 @@ DependencyListenerDelegate del = (DependencyListenerDelegate) m_dependencyToSatisfy[ service ]; del( service, handler ); + + m_dependencyToSatisfy.Remove( service ); } } } 1.2 +17 -0 avalon-sandbox/avalon-net/Castle/MicroKernel/ConstructionInfo.cs Index: ConstructionInfo.cs =================================================================== RCS file: /home/cvs/avalon-sandbox/avalon-net/Castle/MicroKernel/ConstructionInfo.cs,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- ConstructionInfo.cs 31 Mar 2004 00:43:30 -0000 1.1 +++ ConstructionInfo.cs 31 Mar 2004 03:45:08 -0000 1.2 @@ -27,8 +27,12 @@ private Hashtable m_service2handler; + private PropertyInfo[] m_properties = new PropertyInfo[0]; + public ConstructionInfo(ConstructorInfo constructor, Hashtable service2handler) { + AssertUtil.ArgumentNotNull( constructor, "constructor" ); + m_constructor = constructor; m_service2handler = service2handler; } @@ -38,6 +42,19 @@ get { return m_constructor; + } + } + + public PropertyInfo[] Properties + { + get + { + return m_properties; + } + set + { + AssertUtil.ArgumentNotNull( value, "value" ); + m_properties = value; } } 1.2 +6 -0 avalon-sandbox/avalon-net/Castle/MicroKernel/Factory/SimpleComponentFactory.cs Index: SimpleComponentFactory.cs =================================================================== RCS file: /home/cvs/avalon-sandbox/avalon-net/Castle/MicroKernel/Factory/SimpleComponentFactory.cs,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- SimpleComponentFactory.cs 31 Mar 2004 00:43:30 -0000 1.1 +++ SimpleComponentFactory.cs 31 Mar 2004 03:45:08 -0000 1.2 @@ -62,6 +62,12 @@ new AspectInvocationHandler( m_before, m_after, instance ) ); } + foreach( PropertyInfo property in m_info.Properties ) + { + IHandler handler = m_info[ property.PropertyType ]; + property.SetValue( instance, handler.Resolve(), null); + } + return instance; } catch(Exception ex) 1.2 +69 -68 avalon-sandbox/avalon-net/Castle/MicroKernel/Handlers/SimpleHandler.cs Index: SimpleHandler.cs =================================================================== RCS file: /home/cvs/avalon-sandbox/avalon-net/Castle/MicroKernel/Handlers/SimpleHandler.cs,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- SimpleHandler.cs 31 Mar 2004 00:43:30 -0000 1.1 +++ SimpleHandler.cs 31 Mar 2004 03:45:08 -0000 1.2 @@ -21,44 +21,43 @@ /// <summary> /// Summary description for SimpleHandler. /// </summary> - public class SimpleHandler : IHandler + public class SimpleHandler : AbstractHandler { - protected Type m_service; - - protected Type m_implementation; - - protected Kernel m_kernel; - - private State m_state = State.Valid; - private ConstructorInfo m_constructor; - - private ArrayList m_dependencies = new ArrayList(); - - private Hashtable m_serv2handler = new Hashtable(); - - private ILifestyleManager m_lifestyleManager; + + private ArrayList m_properties = new ArrayList(); /// <summary> /// /// </summary> /// <param name="service"></param> /// <param name="implementation"></param> - public SimpleHandler(Type service, Type implementation) + public SimpleHandler(Type service, Type implementation) : base(service, implementation) { - AssertUtil.ArgumentNotNull( service, "service" ); - AssertUtil.ArgumentNotNull( implementation, "implementation" ); - - m_service = service; - m_implementation = implementation; } #region IHandler Members - public void Init( Kernel kernel ) + public override void Init( Kernel kernel ) { - m_kernel = kernel; + base.Init(kernel); + + InspectConstructors(); + InspectSetMethods(); + + // Now we check with the kernel if + // we have the necessary implementations + // for the services requested by the constructor + + EnsureHaveRequiredImplementations(); + + CreateComponentFactoryAndLifestyleManager(); + } + #endregion + + protected void InspectConstructors() + { ConstructorInfo[] constructors = m_implementation.GetConstructors(); // TODO: Try to sort the array @@ -80,50 +79,35 @@ throw new HandlerException( String.Format("Handler could not find an eligible constructor for type {0}", m_implementation.FullName) ); } - - // Now we check with the kernel if - // we have the necessary implementations - // for the services requested by the constructor - - EnsureHaveRequiredImplementations(); - - CreateComponentFactoryAndLifestyleManager(); } - public object Resolve() + protected void InspectSetMethods() { - if (m_state == State.WaitingDependency) - { - throw new HandlerException("Can't Resolve component. It has dependencies to be satisfied."); - } + PropertyInfo[] properties = m_service.GetProperties(); - try + foreach(PropertyInfo property in properties) { - return m_lifestyleManager.Resolve(); - } - catch(Exception ex) - { - throw new HandlerException("Exception while attempting to instantiate type", ex); + if (IsEligible( property )) + { + AddDependency( property.PropertyType ); + + m_properties.Add( property ); + } } } - public void Release() + protected bool IsEligible( PropertyInfo property ) { - m_lifestyleManager.Release(); - } + // TODO: An attribute could say to use + // that the property is optional. - /// <summary> - /// - /// </summary> - public State ActualState - { - get + if (!property.CanWrite || !property.PropertyType.IsInterface) { - return m_state; + return false; } - } - #endregion + return true; + } protected bool IsEligible( ConstructorInfo constructor ) { @@ -132,11 +116,10 @@ foreach(ParameterInfo parameter in parameters) { if (parameter.ParameterType == typeof(String) && - !parameter.Name.Equals("contextdir")) // Just as sample + !parameter.Name.Equals("contextdir")) // Just a sample { return false; } - if (!parameter.ParameterType.IsInterface) { return false; @@ -154,22 +137,39 @@ { if (parameter.ParameterType.IsInterface) { - if (m_kernel.HasService( parameter.ParameterType )) - { - m_serv2handler[parameter.ParameterType] = - m_kernel.GetHandlerForService( parameter.ParameterType ); - } - else - { - m_kernel.AddDependencyListener( parameter.ParameterType, new DependencyListenerDelegate(DependencySatisfied) ); - - m_state = State.WaitingDependency; - m_dependencies.Add( parameter.ParameterType ); - } + AddDependency( parameter.ParameterType ); } } } + protected void AddDependency( Type service ) + { + if (m_kernel.HasService( service )) + { + m_serv2handler[ service ] = m_kernel.GetHandlerForService( service ); + } + else + { + // This is handler is considered invalid + // until dependencies are satisfied + m_state = State.WaitingDependency; + m_dependencies.Add( service ); + + // Register ourself in the kernel + // to be notified if the dependency is satified + m_kernel.AddDependencyListener( + service, + new DependencyListenerDelegate(DependencySatisfied) ); + } + } + + /// <summary> + /// Delegate implementation invoked by kernel + /// when one of registered dependencies were satisfied by + /// new components registered. + /// </summary> + /// <param name="service"></param> + /// <param name="handler"></param> private void DependencySatisfied( Type service, IHandler handler ) { m_serv2handler[ service ] = handler; @@ -185,6 +185,7 @@ private void CreateComponentFactoryAndLifestyleManager() { ConstructionInfo info = new ConstructionInfo( m_constructor, m_serv2handler ); + info.Properties = (PropertyInfo[]) m_properties.ToArray( typeof(PropertyInfo) ); IComponentFactory factory = new Factory.SimpleComponentFactory( m_service, m_implementation, 1.1 avalon-sandbox/avalon-net/Castle/MicroKernel/Handlers/AbstractHandler.cs Index: AbstractHandler.cs =================================================================== // Copyright 2004 The Apache Software Foundation // // 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. namespace Apache.Avalon.Castle.MicroKernel.Handlers { using System; using System.Collections; /// <summary> /// Summary description for AbstractHandler. /// </summary> public abstract class AbstractHandler : IHandler { protected Type m_service; protected Type m_implementation; protected Kernel m_kernel; protected State m_state = State.Valid; protected ArrayList m_dependencies = new ArrayList(); protected Hashtable m_serv2handler = new Hashtable(); protected ILifestyleManager m_lifestyleManager; /// <summary> /// /// </summary> /// <param name="service"></param> /// <param name="implementation"></param> public AbstractHandler(Type service, Type implementation) { AssertUtil.ArgumentNotNull( service, "service" ); AssertUtil.ArgumentNotNull( implementation, "implementation" ); m_service = service; m_implementation = implementation; } #region IHandler Members public virtual void Init( Kernel kernel ) { m_kernel = kernel; } public virtual object Resolve() { if (m_state == State.WaitingDependency) { throw new HandlerException("Can't Resolve component. It has dependencies to be satisfied."); } try { return m_lifestyleManager.Resolve(); } catch(Exception ex) { throw new HandlerException("Exception while attempting to instantiate type", ex); } } public virtual void Release() { m_lifestyleManager.Release(); } /// <summary> /// /// </summary> public virtual State ActualState { get { return m_state; } } #endregion } } 1.2 +1 -1 avalon-sandbox/avalon-net/Castle/MicroKernel/MicroKernelTest/AssemblyInfo.cs Index: AssemblyInfo.cs =================================================================== RCS file: /home/cvs/avalon-sandbox/avalon-net/Castle/MicroKernel/MicroKernelTest/AssemblyInfo.cs,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- AssemblyInfo.cs 31 Mar 2004 00:43:30 -0000 1.1 +++ AssemblyInfo.cs 31 Mar 2004 03:45:08 -0000 1.2 @@ -1,4 +1,4 @@ -// Copyright 2004 Apache Software Foundation +// Copyright 2004 The Apache Software Foundation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. 1.2 +20 -0 avalon-sandbox/avalon-net/Castle/MicroKernel/MicroKernelTest/MicroKernelTest.csproj Index: MicroKernelTest.csproj =================================================================== RCS file: /home/cvs/avalon-sandbox/avalon-net/Castle/MicroKernel/MicroKernelTest/MicroKernelTest.csproj,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- MicroKernelTest.csproj 31 Mar 2004 00:43:30 -0000 1.1 +++ MicroKernelTest.csproj 31 Mar 2004 03:45:08 -0000 1.2 @@ -124,12 +124,32 @@ BuildAction = "Compile" /> <File + RelPath = "Components\ICustomerManager.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "Components\IMailMarketingService.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File RelPath = "Components\IMailService.cs" SubType = "Code" BuildAction = "Compile" /> <File RelPath = "Components\ISpamService.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "Components\SimpleCustomerManager.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "Components\SimpleMailMarketingService.cs" SubType = "Code" BuildAction = "Compile" /> 1.2 +41 -0 avalon-sandbox/avalon-net/Castle/MicroKernel/MicroKernelTest/SimpleComponentFactoryTestCase.cs Index: SimpleComponentFactoryTestCase.cs =================================================================== RCS file: /home/cvs/avalon-sandbox/avalon-net/Castle/MicroKernel/MicroKernelTest/SimpleComponentFactoryTestCase.cs,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- SimpleComponentFactoryTestCase.cs 31 Mar 2004 00:43:30 -0000 1.1 +++ SimpleComponentFactoryTestCase.cs 31 Mar 2004 03:45:08 -0000 1.2 @@ -92,5 +92,46 @@ factory.Etherialize(); } + + [Test] + public void DependencyInSetMethods() + { + Type service = typeof( IMailMarketingService ); + Type implementation = typeof( SimpleMailMarketingService ); + Type serviceDep1 = typeof( IMailService ); + Type implementationDep1 = typeof( SimpleMailService ); + Type serviceDep2 = typeof( ICustomerManager ); + Type implementationDep2 = typeof( SimpleCustomerManager ); + + kernel.AddComponent( "a", service, implementation ); + kernel.AddComponent( "b", serviceDep1, implementationDep1 ); + kernel.AddComponent( "c", serviceDep2, implementationDep2 ); + + ConstructorInfo constructor = + implementation.GetConstructor( Type.EmptyTypes ); + + Hashtable serv2Handler = new Hashtable(); + serv2Handler[ serviceDep1 ] = kernel.GetHandlerForService( serviceDep1 ); + serv2Handler[ serviceDep2 ] = kernel.GetHandlerForService( serviceDep2 ); + + ConstructionInfo info = new ConstructionInfo( constructor, serv2Handler ); + info.Properties = service.GetProperties(); + + SimpleComponentFactory factory = new SimpleComponentFactory( + service, implementation, new IAspect[0], new IAspect[0], info ); + + Object instance = factory.Incarnate(); + + AssertNotNull( instance ); + AssertNotNull( instance as IMailMarketingService ); + + SimpleMailMarketingService mailMarketing = (SimpleMailMarketingService) instance; + AssertNotNull( mailMarketing.m_mailService ); + AssertNotNull( mailMarketing.m_customerManager ); + + mailMarketing.AnnoyMillionsOfPeople( "Say something" ); + + factory.Etherialize(); + } } } 1.1 avalon-sandbox/avalon-net/Castle/MicroKernel/MicroKernelTest/Components/ICustomerManager.cs Index: ICustomerManager.cs =================================================================== // Copyright 2004 The Apache Software Foundation // // 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. namespace Apache.Avalon.Castle.MicroKernel.Test.Components { using System; /// <summary> /// Summary description for ICustomerManager. /// </summary> public interface ICustomerManager { object[] Customers { get; } } } 1.1 avalon-sandbox/avalon-net/Castle/MicroKernel/MicroKernelTest/Components/IMailMarketingService.cs Index: IMailMarketingService.cs =================================================================== // Copyright 2004 The Apache Software Foundation // // 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. namespace Apache.Avalon.Castle.MicroKernel.Test.Components { using System; /// <summary> /// Summary description for IMailMarketingService. /// </summary> public interface IMailMarketingService { ICustomerManager CustomerManager { get; set; } IMailService MailService { get; set; } void AnnoyMillionsOfPeople(String message); } } 1.1 avalon-sandbox/avalon-net/Castle/MicroKernel/MicroKernelTest/Components/SimpleCustomerManager.cs Index: SimpleCustomerManager.cs =================================================================== // Copyright 2004 The Apache Software Foundation // // 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. namespace Apache.Avalon.Castle.MicroKernel.Test.Components { using System; /// <summary> /// Summary description for SimpleCustomerManager. /// </summary> public class SimpleCustomerManager : ICustomerManager { public SimpleCustomerManager() { } #region ICustomerManager Members public object[] Customers { get { return null; } } #endregion } } 1.1 avalon-sandbox/avalon-net/Castle/MicroKernel/MicroKernelTest/Components/SimpleMailMarketingService.cs Index: SimpleMailMarketingService.cs =================================================================== // Copyright 2004 The Apache Software Foundation // // 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. namespace Apache.Avalon.Castle.MicroKernel.Test.Components { using System; /// <summary> /// Summary description for SimpleMailMarketingService. /// </summary> public class SimpleMailMarketingService : IMailMarketingService { public IMailService m_mailService; public ICustomerManager m_customerManager; public SimpleMailMarketingService() { } #region IMailMarketingService Members public ICustomerManager CustomerManager { get { return m_customerManager; } set { m_customerManager = value; } } public IMailService MailService { get { return m_mailService; } set { m_mailService = value; } } public void AnnoyMillionsOfPeople(String message) { if (MailService == null) { throw new NullReferenceException("IMailService wasn't supplied."); } if (CustomerManager == null) { throw new NullReferenceException("ICustomerManager wasn't supplied."); } } #endregion } }
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]