From fb0fb91c7dc37c745708571febc6d34467f483ee Mon Sep 17 00:00:00 2001 From: eeichinger Date: Sun, 10 May 2009 15:44:08 +0000 Subject: [PATCH] fixed SPRNET-1204 --- .../Support/StaticApplicationContext.cs | 11 ++ .../Proxy/AbstractProxyMethodBuilder.cs | 5 +- .../Proxy/CompositionProxyTypeBuilder.cs | 80 ++++++----- .../EnterpriseServicesExporter.cs | 115 ++++++++++++++-- .../ServicedComponentExporter.cs | 119 +++++++++++----- .../ServicedComponentHelper.cs | 12 +- .../ServicedComponentExporterTests.cs | 129 ++++++++++++++++-- .../Spring.Services.Tests.2008.csproj | 6 +- 8 files changed, 385 insertions(+), 92 deletions(-) diff --git a/src/Spring/Spring.Core/Context/Support/StaticApplicationContext.cs b/src/Spring/Spring.Core/Context/Support/StaticApplicationContext.cs index c21c3d22..482a24c2 100644 --- a/src/Spring/Spring.Core/Context/Support/StaticApplicationContext.cs +++ b/src/Spring/Spring.Core/Context/Support/StaticApplicationContext.cs @@ -57,6 +57,17 @@ namespace Spring.Context.Support RegisterSingleton( MessageSourceObjectName, typeof( StaticMessageSource ), null ); } + /// + /// Creates a new, named instance of the StaticApplicationContext class. + /// + /// the context name + /// The parent application context. + public StaticApplicationContext( string name, IApplicationContext parentContext ) + : base( name, true, parentContext ) + { + RegisterSingleton( MessageSourceObjectName, typeof( StaticMessageSource ), null ); + } + /// /// Do nothing: we rely on callers to update our public methods. /// diff --git a/src/Spring/Spring.Core/Proxy/AbstractProxyMethodBuilder.cs b/src/Spring/Spring.Core/Proxy/AbstractProxyMethodBuilder.cs index 391a2d88..0694d39a 100644 --- a/src/Spring/Spring.Core/Proxy/AbstractProxyMethodBuilder.cs +++ b/src/Spring/Spring.Core/Proxy/AbstractProxyMethodBuilder.cs @@ -296,7 +296,10 @@ namespace Spring.Proxy il.EmitCall(OpCodes.Callvirt, interfaceMethod, null); } - private void CallAssertUnderstands(ILGenerator il, MethodInfo method, LocalBuilder targetRef, string targetName) + /// + /// Emits code to ensure that target understands the method and throw a sensible exception otherwise. + /// + protected virtual void CallAssertUnderstands(ILGenerator il, MethodInfo method, LocalBuilder targetRef, string targetName) { // AssertArgumentType il.Emit(OpCodes.Ldloc, targetRef); diff --git a/src/Spring/Spring.Core/Proxy/CompositionProxyTypeBuilder.cs b/src/Spring/Spring.Core/Proxy/CompositionProxyTypeBuilder.cs index b062a0f5..2eaec93b 100644 --- a/src/Spring/Spring.Core/Proxy/CompositionProxyTypeBuilder.cs +++ b/src/Spring/Spring.Core/Proxy/CompositionProxyTypeBuilder.cs @@ -30,15 +30,15 @@ using Spring.Util; namespace Spring.Proxy { - /// - /// Builds a proxy type using composition. - /// - /// - /// - /// In order for this builder to work, the target must implement - /// one or more interfaces. - /// - /// + /// + /// Builds a proxy type using composition. + /// + /// + /// + /// In order for this builder to work, the target must implement + /// one or more interfaces. + /// + /// /// Aleksandar Seovic /// Bruno Baia public class CompositionProxyTypeBuilder : AbstractProxyTypeBuilder @@ -56,17 +56,17 @@ namespace Spring.Proxy #region Properties - /// + /// /// Gets or sets a value indicating whether interfaces should be implemented explicitly. /// /// /// if they should be; otherwise, . /// - public bool ExplicitInterfaceImplementation - { - get { return explicitInterfaceImplementation; } - set { explicitInterfaceImplementation = value; } - } + public bool ExplicitInterfaceImplementation + { + get { return explicitInterfaceImplementation; } + set { explicitInterfaceImplementation = value; } + } #endregion @@ -84,32 +84,32 @@ namespace Spring.Proxy #endregion #region IProxyTypeBuilder Members - + /// - /// Creates a proxy that delegates calls to an instance of the - /// target object. - /// - /// - ///

- /// Only interfaces can be proxied using composition, so the target - /// must implement one or more interfaces. - ///

- ///
+ /// Creates a proxy that delegates calls to an instance of the + /// target object. + ///
+ /// + ///

+ /// Only interfaces can be proxied using composition, so the target + /// must implement one or more interfaces. + ///

+ ///
/// The generated proxy class. - /// - /// If the - /// does not implement any interfaces. - /// + /// + /// If the + /// does not implement any interfaces. + /// public override Type BuildProxyType() { if (Interfaces == null || Interfaces.Length == 0) { throw new ArgumentException( - "Composition proxy target must implement at least one interface."); + "Composition proxy target must implement at least one interface."); } TypeBuilder typeBuilder = CreateTypeBuilder(Name, BaseType); - + // apply custom attributes to the proxy type. ApplyTypeAttributes(typeBuilder, TargetType); @@ -123,15 +123,31 @@ namespace Spring.Proxy foreach (Type intf in Interfaces) { ImplementInterface(typeBuilder, - new TargetProxyMethodBuilder(typeBuilder, this, explicitInterfaceImplementation), + CreateTargetProxyMethodBuilder(typeBuilder), intf, TargetType); } + ImplementCustom(typeBuilder); + return typeBuilder.CreateType(); } + /// + /// Create an to create interface implementations + /// + protected virtual IProxyMethodBuilder CreateTargetProxyMethodBuilder(TypeBuilder typeBuilder) + { + return new TargetProxyMethodBuilder(typeBuilder, this, explicitInterfaceImplementation); + } + #endregion + /// + /// Allows subclasses to generate additional code + /// + protected virtual void ImplementCustom(TypeBuilder builder) + { } + #region IProxyTypeGenerator Members /// diff --git a/src/Spring/Spring.Services/EnterpriseServices/EnterpriseServicesExporter.cs b/src/Spring/Spring.Services/EnterpriseServices/EnterpriseServicesExporter.cs index 089e482b..d71f0b58 100644 --- a/src/Spring/Spring.Services/EnterpriseServices/EnterpriseServicesExporter.cs +++ b/src/Spring/Spring.Services/EnterpriseServices/EnterpriseServicesExporter.cs @@ -278,36 +278,96 @@ namespace Spring.EnterpriseServices /// them with COM+ Component Services. /// public virtual void Export() + { + string assemblyFileName = AppDomain.CurrentDomain.DynamicDirectory.Trim('\\', '/') + "\\" + assemblyName + ".dll"; + FileInfo assemblyFile = new FileInfo(assemblyFileName); + + GenerateComponentAssembly(assemblyFile); + RegisterServicedComponents(assemblyFile); + } + + /// + /// Generates all configured to the given assembly. + /// + public Assembly GenerateComponentAssembly(FileInfo assemblyFile) { AssemblyName an = new AssemblyName(); an.Name = assemblyName; an.Version = new Version("1.0.0.0"); an.KeyPair = new StrongNameKeyPair(GetKeyPair()); - AssemblyBuilder proxyAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.RunAndSave); - ModuleBuilder module = proxyAssembly.DefineDynamicModule(assemblyName, assemblyName + ".dll", true); - ApplyAssemblyAttributes(proxyAssembly); + AssemblyBuilder proxyAssembly = DefineProxyAssembly(an, assemblyFile); + GenerateComponentTypes(proxyAssembly, objectFactory, components, UseSpring); + + // Assembly.Save() does not allow paths... + string dynamicFileName = assemblyFile.Name; + proxyAssembly.Save(dynamicFileName); + Assembly resultAssembly = System.Reflection.Assembly.LoadFrom(assemblyFile.FullName); + return resultAssembly; + } + + /// + /// Generates service types from the list of instances + /// into the given assembly. + /// + /// the assembly to export types to + /// the object factory to resolve target types + /// the list of instances. + /// whether to generate context lookups, + private static AssemblyBuilder GenerateComponentTypes(AssemblyBuilder proxyAssembly, IObjectFactory objectFactory, IList components, bool springManaged) + { + string moduleName = proxyAssembly.GetName().Name; + ModuleBuilder module = proxyAssembly.DefineDynamicModule(moduleName, moduleName + ".dll", true); Type baseType = typeof(ServicedComponent); - if (UseSpring) + if (springManaged) { - baseType = CreateSpringServicedComponentType(module); + baseType = CreateSpringServicedComponentType(module, baseType); } foreach (ServicedComponentExporter definition in components) { - definition.CreateWrapperType(module, baseType, objectFactory, UseSpring); + definition.CreateWrapperType(module, baseType, objectFactory.GetType(definition.TargetName), springManaged); } + return proxyAssembly; + } - proxyAssembly.Save(assemblyName + ".dll"); + private AssemblyBuilder DefineProxyAssembly(AssemblyName an, FileInfo assemblyFile) + { + AssemblyBuilder proxyAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.RunAndSave, assemblyFile.DirectoryName); + ApplyAssemblyAttributes(proxyAssembly); + return proxyAssembly; + } + /// + /// + /// + /// + public void RegisterServicedComponents(FileInfo assemblyFile) + { RegistrationConfig config = new RegistrationConfig(); config.Application = applicationName; - config.AssemblyFile = AppDomain.CurrentDomain.DynamicDirectory + assemblyName + ".dll"; + config.AssemblyFile = assemblyFile.FullName; config.InstallationFlags = InstallationFlags.ReportWarningsToConsole | InstallationFlags.FindOrCreateTargetApplication | InstallationFlags.ReconfigureExistingApplication; RegistrationHelper regHelper = new RegistrationHelper(); regHelper.InstallAssemblyFromConfig(ref config); } + + /// + /// + /// + /// + public void UnregisterServicedComponents(FileInfo assemblyFile) + { + RegistrationConfig config = new RegistrationConfig(); + config.Application = applicationName; + config.AssemblyFile = assemblyFile.FullName; + config.InstallationFlags = InstallationFlags.ReportWarningsToConsole | InstallationFlags.FindOrCreateTargetApplication | InstallationFlags.ReconfigureExistingApplication; + + RegistrationHelper regHelper = new RegistrationHelper(); + regHelper.UninstallAssemblyFromConfig(ref config); + } + #endregion #region Private Methods @@ -406,7 +466,7 @@ namespace Spring.EnterpriseServices /// /// /// - /// internal class SpringServicedComponent + /// internal class SpringServicedComponent: BaseType /// { /// protected delegate object GetObjectHandler(ServicedComponent servicedComponent, string targetName); /// @@ -433,11 +493,16 @@ namespace Spring.EnterpriseServices /// } /// /// - private Type CreateSpringServicedComponentType(ModuleBuilder module) + public static Type CreateSpringServicedComponentType(ModuleBuilder module, Type baseType) { + if (!typeof(ServicedComponent).IsAssignableFrom(baseType)) + { + throw new ArgumentException(string.Format("baseType must derive from {0}, was {1}", typeof(ServicedComponent).FullName, baseType), "baseType"); + } + Type delegateType = DefineDelegate(module); - TypeBuilder typeBuilder = module.DefineType("SpringServicedComponent", System.Reflection.TypeAttributes.Public, typeof(ServicedComponent)); + TypeBuilder typeBuilder = module.DefineType("SpringServicedComponent", System.Reflection.TypeAttributes.Public, baseType); ILGenerator il = null; FieldBuilder getObjectRef = typeBuilder.DefineField("getObject", delegateType, FieldAttributes.Family | FieldAttributes.Static); @@ -448,6 +513,8 @@ namespace Spring.EnterpriseServices Label tryBegin = il.BeginExceptionBlock(); LocalBuilder fldAssembly = il.DeclareLocal(typeof(Assembly)); LocalBuilder fldType = il.DeclareLocal(typeof(Type)); + LocalBuilder fldArgs = il.DeclareLocal(typeof (object[])); + LocalBuilder fldMethod = il.DeclareLocal(typeof (MethodBase)); il.Emit(OpCodes.Call, typeof(Assembly).GetMethod("GetExecutingAssembly")); il.Emit(OpCodes.Callvirt, typeof(Assembly).GetProperty("Location").GetGetMethod()); @@ -484,6 +551,24 @@ namespace Spring.EnterpriseServices il.Emit(OpCodes.Call, typeof(Delegate).GetMethod("CreateDelegate", new Type[] { typeof(Type), typeof(MethodInfo) })); il.Emit(OpCodes.Castclass, delegateType); il.Emit(OpCodes.Stsfld, getObjectRef); + + il.Emit(OpCodes.Ldc_I4_1); + il.Emit(OpCodes.Newarr, typeof(object)); + il.Emit(OpCodes.Stloc, fldArgs); + + il.Emit(OpCodes.Ldloc, fldArgs); + il.Emit(OpCodes.Ldc_I4_0); + il.Emit(OpCodes.Ldtoken, typeBuilder); + il.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle")); + il.Emit(OpCodes.Stelem_Ref); + il.Emit(OpCodes.Ldloc, fldType); + il.Emit(OpCodes.Ldstr, "EnsureComponentContextRegistryInitialized"); + il.Emit(OpCodes.Callvirt, typeof(Type).GetMethod("GetMethod", new Type[] { typeof(string) })); + il.Emit(OpCodes.Ldnull); + il.Emit(OpCodes.Ldloc, fldArgs); + il.Emit(OpCodes.Call, typeof(MethodBase).GetMethod("Invoke", new Type[] { typeof(object), typeof(object[]) })); + il.Emit(OpCodes.Pop); + il.Emit(OpCodes.Leave_S, methodEnd); il.BeginCatchBlock(typeof(Exception)); @@ -497,8 +582,14 @@ namespace Spring.EnterpriseServices private static readonly MethodAttributes ConstructorAttributes = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName; + private void InvokeBal() + { + Type type = typeof (Assembly); + object[] args = new object[] { typeof(EnterpriseServicesExporter) }; + type.GetMethod("MethodName").Invoke(null, args); + } - private Type DefineDelegate(ModuleBuilder module) + private static Type DefineDelegate(ModuleBuilder module) { MethodAttributes methodAtts = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual; diff --git a/src/Spring/Spring.Services/EnterpriseServices/ServicedComponentExporter.cs b/src/Spring/Spring.Services/EnterpriseServices/ServicedComponentExporter.cs index e6373e79..c7c35e6c 100644 --- a/src/Spring/Spring.Services/EnterpriseServices/ServicedComponentExporter.cs +++ b/src/Spring/Spring.Services/EnterpriseServices/ServicedComponentExporter.cs @@ -164,25 +164,49 @@ namespace Spring.EnterpriseServices /// /// Dynamic module builder to use /// - /// Object factory to get target from. - /// whether to generated ApplicationContext lookups or use static target instances - public Type CreateWrapperType(ModuleBuilder module, Type baseType, IObjectFactory objectFactory, bool useSpring) + /// Type of the exported object. + /// whether to generate lookups in ContextRegistry for each service method call or use a 'new'ed target instance + /// + /// if is true, each ServicedComponent method call will look similar to + /// + /// class MyServicedComponent { + /// void MethodX() { + /// ContextRegistry.GetContext().GetObject("TargetName").MethodX(); + /// } + /// } + /// + ///
+ /// if is false, the instance will be simply created at component activation using 'new': + /// + /// class MyServicedComponent { + /// TargetType target = new TargetType(); + /// + /// void MethodX() { + /// target.MethodX(); + /// } + /// } + /// + ///
+ /// The differences are of course that in the former case, the target lifecycle is entirely managed by Spring, thus avoiding + /// issues with ServiceComponent activation/deactivation as well as removing the need for default constructors. + ///
+ public Type CreateWrapperType(ModuleBuilder module, Type baseType, Type targetType, bool springManagedLifecycle) { ValidateConfiguration(); // create wrapper using appropriate proxy builder IProxyTypeBuilder proxyBuilder; - if (useSpring) + if (springManagedLifecycle) { proxyBuilder = new SpringManagedServicedComponentProxyTypeBuilder(module, baseType, this); } else { - proxyBuilder = new SimpleServicedComponentProxyTypeBuilder(module); + proxyBuilder = new SimpleServicedComponentProxyTypeBuilder(module, baseType); } proxyBuilder.Name = _objectName; - proxyBuilder.TargetType = objectFactory.GetType(TargetName); + proxyBuilder.TargetType = targetType; if (_interfaces != null && _interfaces.Length > 0) { proxyBuilder.Interfaces = TypeResolutionUtils.ResolveInterfaceArray(_interfaces); @@ -201,16 +225,70 @@ namespace Spring.EnterpriseServices #region ServicedComponentProxyTypeBuilder inner class definition - private sealed class SimpleServicedComponentProxyTypeBuilder : CompositionProxyTypeBuilder + private class ServicedComponentTargetProxyMethodBuilder : TargetProxyMethodBuilder + { + public ServicedComponentTargetProxyMethodBuilder(TypeBuilder typeBuilder, IProxyTypeGenerator proxyGenerator, bool explicitImplementation) : base(typeBuilder, proxyGenerator, explicitImplementation) + {} + + /// + /// Suppress output to avoid Spring.Core dependency + /// + protected override void CallAssertUnderstands(ILGenerator il, MethodInfo method, LocalBuilder targetRef, string targetName) + { +// base.CallAssertUnderstands(il, method, targetRef, targetName); + } + } + + private class ServicedComponentProxyTypeBuilder : CompositionProxyTypeBuilder + { + /// + /// Implements default constructor for the proxy class. + /// + protected override void ImplementConstructors(TypeBuilder builder) + { + MethodAttributes attributes = MethodAttributes.Public | + MethodAttributes.HideBySig | MethodAttributes.SpecialName | + MethodAttributes.RTSpecialName; + ConstructorBuilder cb = builder.DefineConstructor(attributes, + CallingConventions.Standard, Type.EmptyTypes); + + ILGenerator il = cb.GetILGenerator(); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Call, builder.BaseType.GetConstructor(Type.EmptyTypes)); + il.Emit(OpCodes.Ret); + + ImplementFactoryMethod(builder, cb); + } + + protected void ImplementFactoryMethod(TypeBuilder builder, ConstructorBuilder cb) + { + base.ImplementCustom(builder); + TypeBuilder factory = builder.DefineNestedType("Factory", System.Reflection.TypeAttributes.NestedPublic); + MethodBuilder newMethod = factory.DefineMethod("New", MethodAttributes.Public | MethodAttributes.Static|MethodAttributes.HideBySig, + CallingConventions.Standard, builder, new Type[0]); + ILGenerator il = newMethod.GetILGenerator(); + il.Emit(OpCodes.Newobj, cb); + il.Emit(OpCodes.Ret); + factory.CreateType(); + } + + protected override IProxyMethodBuilder CreateTargetProxyMethodBuilder(TypeBuilder typeBuilder) + { + return new ServicedComponentTargetProxyMethodBuilder(typeBuilder, this, + this.ExplicitInterfaceImplementation); + } + } + + private sealed class SimpleServicedComponentProxyTypeBuilder : ServicedComponentProxyTypeBuilder { private readonly ModuleBuilder module; #region Constructor(s) / Destructor - public SimpleServicedComponentProxyTypeBuilder(ModuleBuilder module) + public SimpleServicedComponentProxyTypeBuilder(ModuleBuilder module, Type baseType) { this.module = module; - BaseType = typeof(ServicedComponent); + BaseType = baseType; } #endregion @@ -225,7 +303,7 @@ namespace Spring.EnterpriseServices #endregion } - private sealed class SpringManagedServicedComponentProxyTypeBuilder : CompositionProxyTypeBuilder + private sealed class SpringManagedServicedComponentProxyTypeBuilder : ServicedComponentProxyTypeBuilder { #region Fields @@ -268,26 +346,6 @@ namespace Spring.EnterpriseServices #region IProxyTypeGenerator Members - /// - /// Implements constructors for the proxy class. - /// - /// - /// The to use. - /// - protected override void ImplementConstructors( TypeBuilder builder ) - { - MethodAttributes attributes = MethodAttributes.Public | - MethodAttributes.HideBySig | MethodAttributes.SpecialName | - MethodAttributes.RTSpecialName; - ConstructorBuilder cb = builder.DefineConstructor( attributes, - CallingConventions.Standard, Type.EmptyTypes ); - - ILGenerator il = cb.GetILGenerator(); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Call, builder.BaseType.GetConstructor(Type.EmptyTypes)); - il.Emit( OpCodes.Ret ); - } - /// /// Generates the IL instructions that pushes /// the target instance on which calls should be delegated to. @@ -301,7 +359,6 @@ namespace Spring.EnterpriseServices il.Emit( OpCodes.Ldstr, this.exporter.TargetName ); il.Emit( OpCodes.Callvirt, getObjectRef.FieldType.GetMethod("Invoke")); } - #endregion } diff --git a/src/Spring/Spring.Services/EnterpriseServices/ServicedComponentHelper.cs b/src/Spring/Spring.Services/EnterpriseServices/ServicedComponentHelper.cs index 59e38d78..c7d32290 100644 --- a/src/Spring/Spring.Services/EnterpriseServices/ServicedComponentHelper.cs +++ b/src/Spring/Spring.Services/EnterpriseServices/ServicedComponentHelper.cs @@ -50,10 +50,10 @@ namespace Spring.EnterpriseServices } /// - /// Reads in the 'xxx.spring-context.xml' configuration file associated with the specified . + /// Reads in the 'xxx.spring-context.xml' configuration file associated with the specified . /// See for an in-depth description on how to export and configure COM+ components. /// - private static void EnsureComponentContextRegistryInitialized(ServicedComponent component) + public static void EnsureComponentContextRegistryInitialized(Type componentType) { if (isInitialized) return; @@ -65,15 +65,15 @@ namespace Spring.EnterpriseServices // this is to ensure, that assemblies place next to the component assembly can be loaded // even when they are not strong named. AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve); - FileInfo componentAssemblyFile = new FileInfo(component.GetType().Assembly.Location); + FileInfo componentAssemblyFile = new FileInfo(componentType.Assembly.Location); componentDirectory = componentAssemblyFile.Directory.FullName; // switch to component assembly's directory (affects resolving relative paths during context instantiation!) Environment.CurrentDirectory = componentDirectory; - // read in config file - ConfigXmlDocument configDoc = new ConfigXmlDocument(); + // read in config file if any FileInfo configFile = new FileInfo(componentAssemblyFile.FullName + ".spring-context.xml"); if (configFile.Exists) { + ConfigXmlDocument configDoc = new ConfigXmlDocument(); configDoc.Load(configFile.FullName); XmlNode configNode = configDoc.SelectSingleNode("//context"); ServicedComponentContextHandler handler = new ServicedComponentContextHandler(); @@ -103,7 +103,7 @@ namespace Spring.EnterpriseServices /// public static object GetObject(ServicedComponent sender, string targetName) { - EnsureComponentContextRegistryInitialized(sender); + EnsureComponentContextRegistryInitialized(sender.GetType()); return ContextRegistry.GetContext().GetObject(targetName); } } diff --git a/test/Spring/Spring.Services.Tests/EnterpriseServices/ServicedComponentExporterTests.cs b/test/Spring/Spring.Services.Tests/EnterpriseServices/ServicedComponentExporterTests.cs index d0837eaa..cb438dc9 100644 --- a/test/Spring/Spring.Services.Tests/EnterpriseServices/ServicedComponentExporterTests.cs +++ b/test/Spring/Spring.Services.Tests/EnterpriseServices/ServicedComponentExporterTests.cs @@ -25,13 +25,19 @@ using System; using System.Collections; using System.EnterpriseServices; +using System.IO; using System.Reflection; using System.Reflection.Emit; - +using System.Runtime.Remoting; +using AopAlliance.Intercept; using NUnit.Framework; using DotNetMock.Dynamic; +using Rhino.Mocks; +using Spring.Aop.Framework; +using Spring.Context.Support; using Spring.Objects.Factory; using Spring.Objects; +using Spring.Objects.Factory.Support; #endregion @@ -44,6 +50,12 @@ namespace Spring.EnterpriseServices [TestFixture] public class ServicedComponentExporterTests { + [TearDown] + public void TearDown() + { + ContextRegistry.Clear(); + } + [Test] [ExpectedException(typeof(ArgumentException))] public void BailsWhenNotConfigured () @@ -62,28 +74,127 @@ namespace Spring.EnterpriseServices exp.TypeAttributes.Add(new TransactionAttribute(TransactionOption.RequiresNew)); exp.AfterPropertiesSet(); - Type type = CreateWrapperType(exp, typeof(TestObject)); + Type type = CreateWrapperType(exp, typeof(TestObject), false); TransactionAttribute[] attrs = (TransactionAttribute[])type.GetCustomAttributes(typeof(TransactionAttribute), false); Assert.AreEqual(1, attrs.Length); Assert.AreEqual(TransactionOption.RequiresNew, attrs[0].Value); } - #region Private helpers classes + [Test] + public void RegistersManagedObjectWithNonDefaultTransactionOption() + { + ServicedComponentExporter exp = new ServicedComponentExporter(); + exp.TargetName = "objectTest"; + exp.ObjectName = "objectTestProxy"; + exp.TypeAttributes = new ArrayList(); + exp.TypeAttributes.Add(new TransactionAttribute(TransactionOption.RequiresNew)); + exp.AfterPropertiesSet(); -// private delegate Type CreateWrapperTypeHandler(ModuleBuilder module, Type baseType, IObjectFactory objectFactory, bool useSpring); + Type type = CreateWrapperType(exp, typeof(TestObject), true); - private Type CreateWrapperType(ServicedComponentExporter exporter, Type type) + TransactionAttribute[] attrs = (TransactionAttribute[])type.GetCustomAttributes(typeof(TransactionAttribute), false); + Assert.AreEqual(1, attrs.Length); + Assert.AreEqual(TransactionOption.RequiresNew, attrs[0].Value); + } + + [Test] + public void CanExportAopProxy() + { + // create an advised proxy and add to objectFactory + // note, that we need to implement a signed interface here + ProxyFactory aopProxyFactory = new ProxyFactory(new Type[] { typeof(IComparable) }); + MockMethodInterceptor methodInterceptor = new MockMethodInterceptor(); + aopProxyFactory.AddAdvice(methodInterceptor); + IComparable aopProxy = (IComparable) aopProxyFactory.GetProxy(); +// ((AssemblyBuilder)aopProxy.GetType().Assembly).Save("Spring.Proxy.dll"); + + // sanity check + methodInterceptor.NextResult = 2; + Assert.AreEqual(methodInterceptor.NextResult, aopProxy.CompareTo(this)); + Assert.AreEqual(1, methodInterceptor.Calls); + + StaticApplicationContext appCtx = new StaticApplicationContext(AbstractApplicationContext.DefaultRootContextName, null); + appCtx.ObjectFactory.RegisterSingleton("objectTest", aopProxy); + + FileInfo assemblyFile = new FileInfo("ServiceComponentExporterTests.TestServicedComponents.dll"); + EnterpriseServicesExporter exporter = new EnterpriseServicesExporter(); + Type serviceType = ExportObject(exporter, assemblyFile, appCtx, "objectTest"); + try + { + // ServiceComponent will obtain its target from root context + ContextRegistry.RegisterContext(appCtx); + methodInterceptor.Calls = 0; + + IComparable testObject; + testObject = (IComparable)Activator.CreateInstance(serviceType); + methodInterceptor.NextResult = 3; + Assert.AreEqual(methodInterceptor.NextResult, testObject.CompareTo(null)); + testObject = (IComparable)Activator.CreateInstance(serviceType); + methodInterceptor.NextResult = 4; + Assert.AreEqual(methodInterceptor.NextResult, testObject.CompareTo(null)); + + Assert.AreEqual(2, methodInterceptor.Calls); + } + finally + { + exporter.UnregisterServicedComponents(assemblyFile); + ContextRegistry.Clear(); + } + } + + private Type ExportObject(EnterpriseServicesExporter exporter, FileInfo assemblyFile, StaticApplicationContext appCtx, string objectName) + { + exporter.ObjectFactory = appCtx.ObjectFactory; + exporter.Assembly = Path.GetFileNameWithoutExtension(assemblyFile.Name); + exporter.ApplicationName = exporter.Assembly; + exporter.ActivationMode = ActivationOption.Library; + exporter.UseSpring = true; + + ServicedComponentExporter exp = new ServicedComponentExporter(); + exp.TargetName = objectName; + exp.ObjectName = objectName + "Service"; + exp.TypeAttributes = new ArrayList(); + exp.TypeAttributes.Add(new TransactionAttribute(TransactionOption.RequiresNew)); + exp.AfterPropertiesSet(); + + exporter.Components.Add(exp); + + Assembly assembly = exporter.GenerateComponentAssembly(assemblyFile); + exporter.RegisterServicedComponents(assemblyFile); + return assembly.GetType(objectName + "Service"); + } + + #region Private helpers & classes + + private class MockMethodInterceptor : IMethodInterceptor + { + public object NextResult = null; + public int Calls = 0; + public IMethodInvocation LastInvocation; + + public object Invoke(IMethodInvocation invocation) + { + Calls++; + LastInvocation = invocation; + return NextResult; + } + } + + private Type CreateWrapperType(ServicedComponentExporter exporter, Type targetType, bool useSpring) { AssemblyName an = new AssemblyName(); - an.Name = "Spring.EnterpriseServices.Tests"; + an.Name = "Spring.EnterpriseServices.Tests"; AssemblyBuilder proxyAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.RunAndSave); ModuleBuilder module = proxyAssembly.DefineDynamicModule(an.Name, an.Name + ".dll", true); - IDynamicMock mockObjectFactory = new DynamicMock(typeof(IObjectFactory)); - mockObjectFactory.ExpectAndReturn("GetType", type, new object[]{ exporter.TargetName }); + Type baseType = typeof (ServicedComponent); + if (useSpring) + { + baseType = EnterpriseServicesExporter.CreateSpringServicedComponentType(module, baseType); + } - object result = exporter.CreateWrapperType(module, typeof(ServicedComponent), (IObjectFactory) mockObjectFactory.Object, false); //createWrapperTypeMethodInfo.Invoke(exporter, new object[] { module, mockObjectFactory.Object }); + object result = exporter.CreateWrapperType(module, baseType, targetType, useSpring); Assert.IsNotNull(result); Assert.IsTrue(result is Type); diff --git a/test/Spring/Spring.Services.Tests/Spring.Services.Tests.2008.csproj b/test/Spring/Spring.Services.Tests/Spring.Services.Tests.2008.csproj index c4d349cd..25922fd8 100644 --- a/test/Spring/Spring.Services.Tests/Spring.Services.Tests.2008.csproj +++ b/test/Spring/Spring.Services.Tests/Spring.Services.Tests.2008.csproj @@ -1,7 +1,7 @@  Local - 9.0.21022 + 9.0.30729 2.0 {4374F018-9738-46BF-A399-4594CEE75B21} Debug @@ -80,6 +80,10 @@ nunit.framework ..\..\..\lib\Net\2.0\nunit.framework.dll + + False + ..\..\..\lib\Net\2.0\Rhino.Mocks.dll + System