From 8a88b425eedb6927668329a5e537ea44b3335976 Mon Sep 17 00:00:00 2001 From: bbaia Date: Sun, 9 Nov 2008 07:34:12 +0000 Subject: [PATCH] Finalized WCF ServiceExporter [SPRNET-1001]. Polished WCF integration + QuickStart + Doc. --- doc/reference/src/wcf.xml | 3 +- .../Spring.WcfQuickStart.2008.sln | 45 +- .../Spring.WcfQuickStart.ClientApp/App.config | 10 +- .../Config/{Web.xml => ServerWeb.xml} | 8 +- ...Spring.WcfQuickStart.ClientApp.2008.csproj | 4 +- ...Spring.WcfQuickStart.ServerApp.2008.csproj | 5 +- .../Calculator.svc | 0 .../CalculatorService.cs | 0 .../ICalculator.cs | 0 .../Properties/AssemblyInfo.cs | 35 + ...Spring.WcfQuickStart.ServerWeb.2008.csproj | 88 +++ .../Spring.WcfQuickStart.ServerWeb/Web.config | 75 ++ .../Bin/Common.Logging.dll.refresh | Bin 104 -> 0 bytes .../Bin/Spring.Core.dll.refresh | Bin 98 -> 0 bytes .../Bin/Spring.Services.dll.refresh | Bin 106 -> 0 bytes .../Bin/antlr.runtime.dll.refresh | Bin 102 -> 0 bytes .../Spring.WcfQuickStart.Web.2008/Web.config | 134 ---- .../Activation/ServiceHostFactoryObject.cs | 58 +- .../ServiceModel/ServiceExporter.cs | 712 ++++++++++++------ .../ServiceModel/SpringServiceHost.cs | 20 +- .../Support/ServiceProxyTypeBuilder.cs | 51 +- .../ServiceModel/ServiceExporterTests.cs | 263 ++++--- 22 files changed, 918 insertions(+), 593 deletions(-) rename examples/Spring/Spring.WcfQuickStart/src/Spring.WcfQuickStart.ClientApp/Config/{Web.xml => ServerWeb.xml} (63%) rename examples/Spring/Spring.WcfQuickStart/src/{Spring.WcfQuickStart.Web.2008 => Spring.WcfQuickStart.ServerWeb}/Calculator.svc (100%) rename examples/Spring/Spring.WcfQuickStart/src/{Spring.WcfQuickStart.Web.2008/App_Code => Spring.WcfQuickStart.ServerWeb}/CalculatorService.cs (100%) rename examples/Spring/Spring.WcfQuickStart/src/{Spring.WcfQuickStart.Web.2008/App_Code => Spring.WcfQuickStart.ServerWeb}/ICalculator.cs (100%) create mode 100644 examples/Spring/Spring.WcfQuickStart/src/Spring.WcfQuickStart.ServerWeb/Properties/AssemblyInfo.cs create mode 100644 examples/Spring/Spring.WcfQuickStart/src/Spring.WcfQuickStart.ServerWeb/Spring.WcfQuickStart.ServerWeb.2008.csproj create mode 100644 examples/Spring/Spring.WcfQuickStart/src/Spring.WcfQuickStart.ServerWeb/Web.config delete mode 100644 examples/Spring/Spring.WcfQuickStart/src/Spring.WcfQuickStart.Web.2008/Bin/Common.Logging.dll.refresh delete mode 100644 examples/Spring/Spring.WcfQuickStart/src/Spring.WcfQuickStart.Web.2008/Bin/Spring.Core.dll.refresh delete mode 100644 examples/Spring/Spring.WcfQuickStart/src/Spring.WcfQuickStart.Web.2008/Bin/Spring.Services.dll.refresh delete mode 100644 examples/Spring/Spring.WcfQuickStart/src/Spring.WcfQuickStart.Web.2008/Bin/antlr.runtime.dll.refresh delete mode 100644 examples/Spring/Spring.WcfQuickStart/src/Spring.WcfQuickStart.Web.2008/Web.config diff --git a/doc/reference/src/wcf.xml b/doc/reference/src/wcf.xml index e2f31fcd..62570f66 100644 --- a/doc/reference/src/wcf.xml +++ b/doc/reference/src/wcf.xml @@ -242,8 +242,7 @@ </object> - This will be shortened using a custom namespce in the 1.2 final - release + This will be shortened using a custom namespce in a future release The value 'serverAppCalculatorEndpoint' refers to the name of an diff --git a/examples/Spring/Spring.WcfQuickStart/Spring.WcfQuickStart.2008.sln b/examples/Spring/Spring.WcfQuickStart/Spring.WcfQuickStart.2008.sln index aed1ad11..7ae62e54 100644 --- a/examples/Spring/Spring.WcfQuickStart/Spring.WcfQuickStart.2008.sln +++ b/examples/Spring/Spring.WcfQuickStart/Spring.WcfQuickStart.2008.sln @@ -2,31 +2,14 @@ Microsoft Visual Studio Solution File, Format Version 10.00 # Visual Studio 2008 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.WcfQuickStart.ClientApp.2008", "src\Spring.WcfQuickStart.ClientApp\Spring.WcfQuickStart.ClientApp.2008.csproj", "{D58B3101-4433-49C5-B41B-51134424FC42}" -EndProject -Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "Spring.WcfQuickStart.Web.2008", "src\Spring.WcfQuickStart.Web.2008\", "{B71CEEE1-9C92-4C79-94BE-46D6D4CDE95A}" - ProjectSection(WebsiteProperties) = preProject - TargetFramework = "3.5" - ProjectReferences = "" - Debug.AspNetCompiler.VirtualPath = "/Spring.WcfQuickStart.Web.2008" - Debug.AspNetCompiler.PhysicalPath = "src\Spring.WcfQuickStart.Web.2008\" - Debug.AspNetCompiler.TargetPath = "PrecompiledWeb\Spring.WcfQuickStart.Web.2008\" - Debug.AspNetCompiler.Updateable = "true" - Debug.AspNetCompiler.ForceOverwrite = "true" - Debug.AspNetCompiler.FixedNames = "false" - Debug.AspNetCompiler.Debug = "True" - Release.AspNetCompiler.VirtualPath = "/Spring.WcfQuickStart.Web.2008" - Release.AspNetCompiler.PhysicalPath = "src\Spring.WcfQuickStart.Web.2008\" - Release.AspNetCompiler.TargetPath = "PrecompiledWeb\Spring.WcfQuickStart.Web.2008\" - Release.AspNetCompiler.Updateable = "true" - Release.AspNetCompiler.ForceOverwrite = "true" - Release.AspNetCompiler.FixedNames = "false" - Release.AspNetCompiler.Debug = "False" - VWDPort = "2637" - DefaultWebSiteLanguage = "Visual C#" + ProjectSection(ProjectDependencies) = postProject + {EEC5B531-104B-44E6-975B-44C0C8973405} = {EEC5B531-104B-44E6-975B-44C0C8973405} EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.WcfQuickStart.ServerApp.2008", "src\Spring.WcfQuickStart.ServerApp\Spring.WcfQuickStart.ServerApp.2008.csproj", "{27AAD98C-4281-4DD4-9E20-1371C58C5283}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.WcfQuickStart.ServerWeb.2008", "src\Spring.WcfQuickStart.ServerWeb\Spring.WcfQuickStart.ServerWeb.2008.csproj", "{EEC5B531-104B-44E6-975B-44C0C8973405}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|.NET = Debug|.NET @@ -47,16 +30,6 @@ Global {D58B3101-4433-49C5-B41B-51134424FC42}.Release|Any CPU.Build.0 = Release|Any CPU {D58B3101-4433-49C5-B41B-51134424FC42}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {D58B3101-4433-49C5-B41B-51134424FC42}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {B71CEEE1-9C92-4C79-94BE-46D6D4CDE95A}.Debug|.NET.ActiveCfg = Debug|.NET - {B71CEEE1-9C92-4C79-94BE-46D6D4CDE95A}.Debug|.NET.Build.0 = Debug|.NET - {B71CEEE1-9C92-4C79-94BE-46D6D4CDE95A}.Debug|Any CPU.ActiveCfg = Debug|.NET - {B71CEEE1-9C92-4C79-94BE-46D6D4CDE95A}.Debug|Mixed Platforms.ActiveCfg = Debug|.NET - {B71CEEE1-9C92-4C79-94BE-46D6D4CDE95A}.Debug|Mixed Platforms.Build.0 = Debug|.NET - {B71CEEE1-9C92-4C79-94BE-46D6D4CDE95A}.Release|.NET.ActiveCfg = Debug|.NET - {B71CEEE1-9C92-4C79-94BE-46D6D4CDE95A}.Release|.NET.Build.0 = Debug|.NET - {B71CEEE1-9C92-4C79-94BE-46D6D4CDE95A}.Release|Any CPU.ActiveCfg = Debug|.NET - {B71CEEE1-9C92-4C79-94BE-46D6D4CDE95A}.Release|Mixed Platforms.ActiveCfg = Debug|.NET - {B71CEEE1-9C92-4C79-94BE-46D6D4CDE95A}.Release|Mixed Platforms.Build.0 = Debug|.NET {27AAD98C-4281-4DD4-9E20-1371C58C5283}.Debug|.NET.ActiveCfg = Debug|Any CPU {27AAD98C-4281-4DD4-9E20-1371C58C5283}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {27AAD98C-4281-4DD4-9E20-1371C58C5283}.Debug|Any CPU.Build.0 = Debug|Any CPU @@ -67,6 +40,16 @@ Global {27AAD98C-4281-4DD4-9E20-1371C58C5283}.Release|Any CPU.Build.0 = Release|Any CPU {27AAD98C-4281-4DD4-9E20-1371C58C5283}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {27AAD98C-4281-4DD4-9E20-1371C58C5283}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {EEC5B531-104B-44E6-975B-44C0C8973405}.Debug|.NET.ActiveCfg = Debug|Any CPU + {EEC5B531-104B-44E6-975B-44C0C8973405}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EEC5B531-104B-44E6-975B-44C0C8973405}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EEC5B531-104B-44E6-975B-44C0C8973405}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {EEC5B531-104B-44E6-975B-44C0C8973405}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {EEC5B531-104B-44E6-975B-44C0C8973405}.Release|.NET.ActiveCfg = Release|Any CPU + {EEC5B531-104B-44E6-975B-44C0C8973405}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EEC5B531-104B-44E6-975B-44C0C8973405}.Release|Any CPU.Build.0 = Release|Any CPU + {EEC5B531-104B-44E6-975B-44C0C8973405}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {EEC5B531-104B-44E6-975B-44C0C8973405}.Release|Mixed Platforms.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/examples/Spring/Spring.WcfQuickStart/src/Spring.WcfQuickStart.ClientApp/App.config b/examples/Spring/Spring.WcfQuickStart/src/Spring.WcfQuickStart.ClientApp/App.config index 56611f5f..9e3d7997 100644 --- a/examples/Spring/Spring.WcfQuickStart/src/Spring.WcfQuickStart.ClientApp/App.config +++ b/examples/Spring/Spring.WcfQuickStart/src/Spring.WcfQuickStart.ClientApp/App.config @@ -11,10 +11,10 @@ - - + + - + @@ -42,8 +42,8 @@ - - - - + diff --git a/examples/Spring/Spring.WcfQuickStart/src/Spring.WcfQuickStart.ClientApp/Spring.WcfQuickStart.ClientApp.2008.csproj b/examples/Spring/Spring.WcfQuickStart/src/Spring.WcfQuickStart.ClientApp/Spring.WcfQuickStart.ClientApp.2008.csproj index 2152fa3e..6e4cf2c4 100644 --- a/examples/Spring/Spring.WcfQuickStart/src/Spring.WcfQuickStart.ClientApp/Spring.WcfQuickStart.ClientApp.2008.csproj +++ b/examples/Spring/Spring.WcfQuickStart/src/Spring.WcfQuickStart.ClientApp/Spring.WcfQuickStart.ClientApp.2008.csproj @@ -72,7 +72,6 @@ - @@ -94,6 +93,9 @@ false + + + + + + + + False + False + 2637 + /Spring.WcfQuickStart.ServerWeb/ + + + False + False + + + + + \ No newline at end of file diff --git a/examples/Spring/Spring.WcfQuickStart/src/Spring.WcfQuickStart.ServerWeb/Web.config b/examples/Spring/Spring.WcfQuickStart/src/Spring.WcfQuickStart.ServerWeb/Web.config new file mode 100644 index 00000000..84ddbdb0 --- /dev/null +++ b/examples/Spring/Spring.WcfQuickStart/src/Spring.WcfQuickStart.ServerWeb/Web.config @@ -0,0 +1,75 @@ + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Spring/Spring.WcfQuickStart/src/Spring.WcfQuickStart.Web.2008/Bin/Common.Logging.dll.refresh b/examples/Spring/Spring.WcfQuickStart/src/Spring.WcfQuickStart.Web.2008/Bin/Common.Logging.dll.refresh deleted file mode 100644 index 7ae4334d7a3e44bf17fb4f7da2c6075a14226e49..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 104 zcmezWPme*5AqI%ClSvGj40%BLJcd+;5+H2^RA&IBQ-J&=hEj%fAlsQCpCK0r^MR`L U7<_b%7 diff --git a/examples/Spring/Spring.WcfQuickStart/src/Spring.WcfQuickStart.Web.2008/Bin/Spring.Services.dll.refresh b/examples/Spring/Spring.WcfQuickStart/src/Spring.WcfQuickStart.Web.2008/Bin/Spring.Services.dll.refresh deleted file mode 100644 index 0fe878ac6d96a759a4c28b15bdd3322115698c92..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 106 zcmZvSK?;B{3EL#f|y9q$s7-OcPAL9n_1H<79G%1KvOdy9IQ6Azbu Smv#zG*8F`bbK3}e#d!hK&l1`I diff --git a/examples/Spring/Spring.WcfQuickStart/src/Spring.WcfQuickStart.Web.2008/Web.config b/examples/Spring/Spring.WcfQuickStart/src/Spring.WcfQuickStart.Web.2008/Web.config deleted file mode 100644 index 7db920ba..00000000 --- a/examples/Spring/Spring.WcfQuickStart/src/Spring.WcfQuickStart.Web.2008/Web.config +++ /dev/null @@ -1,134 +0,0 @@ - - - - - -
-
- - - -
- -
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Spring/Spring.Services/ServiceModel/Activation/ServiceHostFactoryObject.cs b/src/Spring/Spring.Services/ServiceModel/Activation/ServiceHostFactoryObject.cs index 9d969420..14ccdac2 100644 --- a/src/Spring/Spring.Services/ServiceModel/Activation/ServiceHostFactoryObject.cs +++ b/src/Spring/Spring.Services/ServiceModel/Activation/ServiceHostFactoryObject.cs @@ -23,7 +23,7 @@ using System; using System.ServiceModel; -using Spring.Context; + using Spring.Objects.Factory; #endregion @@ -35,8 +35,7 @@ namespace Spring.ServiceModel.Activation /// to host objects created with Spring's IoC container. /// /// Bruno Baia - /// $Id: ServiceHostFactory.cs,v 1.2 2007/05/18 21:04:37 bbaia Exp $ - public class ServiceHostFactoryObject : IFactoryObject, IApplicationContextAware, IInitializingObject, IDisposable + public class ServiceHostFactoryObject : IFactoryObject, IInitializingObject, IObjectFactoryAware, IDisposable { #region Logging @@ -48,7 +47,11 @@ namespace Spring.ServiceModel.Activation private string _targetName; private Uri[] _baseAddresses = new Uri[] { }; - private IApplicationContext _applicationContext; + + /// + /// The owning factory. + /// + protected IObjectFactory objectFactory; /// /// The instance managed by this factory. @@ -97,38 +100,30 @@ namespace Spring.ServiceModel.Activation #endregion - #region IApplicationContextAware Members + #region IObjectFactoryAware Members /// - /// Sets the that this - /// object runs in. + /// Callback that supplies the owning factory to an object instance. /// - /// + /// + /// Owning + /// (may not be ). The object can immediately + /// call methods on the factory. + /// /// ///

- /// Normally this call will be used to initialize the object. - ///

- ///

- /// Invoked after population of normal object properties but before an - /// init callback such as - /// 's + /// Invoked after population of normal object properties but before an init + /// callback like 's /// - /// or a custom init-method. Invoked after the setting of any - /// 's - /// - /// property. + /// method or a custom init-method. ///

///
- /// - /// In the case of application context initialization errors. - /// /// - /// If thrown by any application context methods. + /// In case of initialization errors. /// - /// - public IApplicationContext ApplicationContext + public virtual IObjectFactory ObjectFactory { - set { _applicationContext = value; } + set { this.objectFactory = value; } } #endregion @@ -170,13 +165,13 @@ namespace Spring.ServiceModel.Activation #region IInitializingObject Members /// - /// Publish the object + /// Publish the object. /// - public void AfterPropertiesSet() + public virtual void AfterPropertiesSet() { ValidateConfiguration(); - springServiceHost = new SpringServiceHost(TargetName, _applicationContext, BaseAddresses); + springServiceHost = new SpringServiceHost(TargetName, objectFactory, BaseAddresses); springServiceHost.Open(); @@ -207,9 +202,12 @@ namespace Spring.ServiceModel.Activation #endregion - #region Private Methods + #region Protected Methods - private void ValidateConfiguration() + /// + /// Validates the configuration. + /// + protected virtual void ValidateConfiguration() { if (TargetName == null) { diff --git a/src/Spring/Spring.Services/ServiceModel/ServiceExporter.cs b/src/Spring/Spring.Services/ServiceModel/ServiceExporter.cs index 90f05315..2f717dcb 100644 --- a/src/Spring/Spring.Services/ServiceModel/ServiceExporter.cs +++ b/src/Spring/Spring.Services/ServiceModel/ServiceExporter.cs @@ -1,221 +1,493 @@ -#if NET_3_0 -#region License - -/* - * Copyright © 2002-2007 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; - -using Spring.Util; -using Spring.Context; -using Spring.Core.TypeResolution; -using Spring.Objects; -using Spring.Objects.Factory; -using Spring.Objects.Factory.Support; -using Spring.ServiceModel.Support; -using Spring.Context.Support; -using Spring.Proxy; - -#endregion - -namespace Spring.ServiceModel -{ - /// - /// Registers an object type on the server as a WCF service. - /// - /// Bruno Baia - public class ServiceExporter : IApplicationContextAware, IObjectFactoryAware, IInitializingObject, IDisposable - { - #region Logging - - private static readonly Common.Logging.ILog LOG = Common.Logging.LogManager.GetLogger(typeof(ServiceExporter)); - - #endregion - - #region Fields - - private string targetName; - private string[] _contracts; - private IList _typeAttributes = new ArrayList(); - private IDictionary _memberAttributes = new Hashtable(); - - private IApplicationContext applicationContext; - private AbstractObjectFactory objectFactory; - - private System.ServiceModel.ServiceHost serviceHost; - - #endregion - - #region Constructor(s) / Destructor - - /// - /// Creates a new instance of the class. - /// - public ServiceExporter() - { - } - - #endregion - - #region Properties - - /// - /// Gets or sets the name of the target object definition. - /// - public string TargetName - { - get { return targetName; } - set { targetName = value; } - } - - /// - /// Gets or sets the list of service contracts. - /// - /// - /// If not set, all the interfaces implemented or inherited - /// by the target type will be used. - /// - /// The interfaces. - public string[] Contracts - { - get { return _contracts; } - set { _contracts = value; } - } - - /// - /// Gets or sets a list of custom attributes - /// that should be applied to the WCF service class. - /// - public IList TypeAttributes - { - get { return _typeAttributes; } - set { _typeAttributes = value; } - } - - /// - /// Gets or sets a dictionary of custom attributes - /// that should be applied to the WCF service members. - /// - /// - /// Dictionary key is an expression that members can be matched against. - /// Value is a list of attributes that should be applied - /// to each member that matches expression. - /// - public IDictionary MemberAttributes - { - get { return _memberAttributes; } - set { _memberAttributes = value; } - } - - #endregion - - #region IApplicationContextAware Members - - /// - /// Gets or sets the that this - /// object runs in. - /// - public IApplicationContext ApplicationContext - { - get { return applicationContext; } - set { applicationContext = value; } - } - - #endregion - - #region IObjectFactoryAware Members - - /// - /// Sets object factory to use. - /// - public IObjectFactory ObjectFactory - { - set { objectFactory = (AbstractObjectFactory) value; } - } - - #endregion - - #region IInitializingObject Members - - /// - /// Publish the object - /// - public void AfterPropertiesSet() - { - ValidateConfiguration(); - Export(); - } - - #endregion - - #region IDisposable Members - - /// - /// Closes the service host. - /// - public void Dispose() - { - serviceHost.Close(); - } - - #endregion - - #region Private Methods - - private void ValidateConfiguration() - { - if (TargetName == null) - { - throw new ArgumentException("The TargetName property is required."); - } - } - - private void Export() - { - string contextName = (applicationContext.Name == AbstractApplicationContext.DefaultRootContextName) ? null : applicationContext.Name; - Type targetType = objectFactory.GetType(targetName); - IProxyTypeBuilder builder = new ServiceProxyTypeBuilder(targetName, applicationContext, targetType); - if (Contracts != null && Contracts.Length > 0) - { - builder.Interfaces = TypeResolutionUtils.ResolveInterfaceArray(Contracts); - } - builder.TypeAttributes = TypeAttributes; - builder.MemberAttributes = MemberAttributes; - - Type serviceType = builder.BuildProxyType(); - - serviceHost = new System.ServiceModel.ServiceHost(serviceType); - serviceHost.Open(); - - #region Instrumentation - - if (LOG.IsDebugEnabled) - { - LOG.Debug(String.Format("The service '{0}' is ready and can now be accessed.", targetName)); - } - - #endregion - } - - #endregion - } -} +#if NET_3_0 +#region License + +/* + * Copyright © 2002-2008 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; +using System.Reflection; +using System.Reflection.Emit; +using System.ServiceModel; +using System.Net.Security; + +using Spring.Util; +using Spring.Context; +using Spring.Core.TypeResolution; +using Spring.Objects; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Support; +using Spring.Proxy; +using Spring.ServiceModel.Support; + +#endregion + +namespace Spring.ServiceModel +{ + /// + /// Exports an object as a WCF service. + /// + /// Bruno Baia + public class ServiceExporter : IFactoryObject, IInitializingObject, IObjectFactoryAware, IObjectNameAware + { + #region Logging + + private static readonly Common.Logging.ILog LOG = Common.Logging.LogManager.GetLogger(typeof(ServiceExporter)); + + #endregion + + #region Fields + + private string _targetName; + private Type _contractInterface; + private IList _typeAttributes = new ArrayList(); + private IDictionary _memberAttributes = new Hashtable(); + + private string _name; + private string _namespace; + private string _configurationName; + private Type _callbackContract; + private ProtectionLevel _protectionLevel; + private SessionMode _sessionMode; + + /// + /// The name of the object in the factory. + /// + protected string objectName; + + /// + /// The owning factory. + /// + protected IObjectFactory objectFactory; + + /// + /// The generated WCF service wrapper type. + /// + private Type proxyType; + + #endregion + + #region Constructor(s) / Destructor + + /// + /// Creates a new instance of the class. + /// + public ServiceExporter() + { + } + + #endregion + + #region Properties + + /// + /// Gets or sets the name of the target object definition. + /// + public string TargetName + { + get { return _targetName; } + set { _targetName = value; } + } + + /// + /// Gets or sets the service contract interface type. + /// + /// + /// If not set, uses the unique interface implemented or inherited by the target type. + /// An error will be thrown if the target type implements more than one interface. + /// + /// The service contract interface type. + public Type ContractInterface + { + get { return _contractInterface; } + set { _contractInterface = value; } + } + + /// + /// Gets or sets a list of custom attributes + /// that should be applied to the WCF service class. + /// + public IList TypeAttributes + { + get { return _typeAttributes; } + set { _typeAttributes = value; } + } + + /// + /// Gets or sets a dictionary of custom attributes + /// that should be applied to the WCF service members. + /// + /// + /// Dictionary key is an expression that members can be matched against. + /// Value is a list of attributes that should be applied + /// to each member that matches expression. + /// + public IDictionary MemberAttributes + { + get { return _memberAttributes; } + set { _memberAttributes = value; } + } + + /// + /// Gets or sets the name for the <portType> element in + /// Web Services Description Language (WSDL). + /// + /// + /// The default value is the name of the class or interface to which the + /// System.ServiceModel.ServiceContractAttribute is applied. + /// + public string Name + { + get { return _name; } + set { _name = value; } + } + + /// + /// Gets or sets the namespace of the <portType> element in + /// Web Services Description Language (WSDL). + /// + /// + /// The WSDL namespace of the <portType> element. The default value is "http://tempuri.org". + /// + public string Namespace + { + get { return _namespace; } + set { _namespace = value; } + } + + /// + /// Gets or sets the name used to locate the service in an application configuration file. + /// + /// + /// The name used to locate the service element in an application configuration file. + /// The default is the name of the service implementation class. + /// + public string ConfigurationName + { + get { return _configurationName; } + set { _configurationName = value; } + } + + /// + /// Gets or sets the type of callback contract when the contract is a duplex contract. + /// + /// + /// A that indicates the callback contract. The default is null. + /// + public Type CallbackContract + { + get { return _callbackContract; } + set { _callbackContract = value; } + } + + /// + /// Specifies whether the binding for the contract must support the value of + /// the ProtectionLevel property. + /// + /// + /// One of the values. + /// The default is . + /// + public ProtectionLevel ProtectionLevel + { + get { return _protectionLevel; } + set { _protectionLevel = value; } + } + + /// + /// Gets or sets whether sessions are allowed, not allowed or required. + /// + /// + /// A that indicates whether sessions are allowed, + /// not allowed, or required. + /// + public SessionMode SessionMode + { + get { return _sessionMode; } + set { _sessionMode = value; } + } + + #endregion + + #region IObjectFactoryAware Members + + /// + /// Callback that supplies the owning factory to an object instance. + /// + /// + /// Owning + /// (may not be ). The object can immediately + /// call methods on the factory. + /// + /// + ///

+ /// Invoked after population of normal object properties but before an init + /// callback like 's + /// + /// method or a custom init-method. + ///

+ ///
+ /// + /// In case of initialization errors. + /// + public virtual IObjectFactory ObjectFactory + { + set { this.objectFactory = value; } + } + + #endregion + + #region IInitializingObject Members + + /// + /// Publish the object + /// + public void AfterPropertiesSet() + { + ValidateConfiguration(); + GenerateProxy(); + } + + #endregion + + #region IFactoryObject Members + + /// + /// Return an instance (possibly shared or independent) of the object + /// managed by this factory. + /// + /// + /// + /// If this method is being called in the context of an enclosing IoC container and + /// returns , the IoC container will consider this factory + /// object as not being fully initialized and throw a corresponding (and most + /// probably fatal) exception. + /// + /// + /// + /// An instance (possibly shared or independent) of the object managed by + /// this factory. + /// + public object GetObject() + { + return proxyType; + } + + /// + /// Return the of object that this + /// creates, or + /// if not known in advance. + /// + public Type ObjectType + { + get { return typeof(Type); } + } + + /// + /// Is the object managed by this factory a singleton or a prototype? + /// + public bool IsSingleton + { + get { return false; } + } + + #endregion + + #region IObjectNameAware Members + + /// + /// Set the name of the object in the object factory that created this object. + /// + /// + /// The name of the object in the factory. + /// + /// + ///

+ /// Invoked after population of normal object properties but before an init + /// callback like 's + /// + /// method or a custom init-method. + ///

+ ///
+ public string ObjectName + { + set { this.objectName = value; } + } + + #endregion + + #region Protected Methods + + /// + /// Validates the configuration. + /// + protected virtual void ValidateConfiguration() + { + if (TargetName == null) + { + throw new ArgumentException("The TargetName property is required."); + } + if (ContractInterface != null && !ContractInterface.IsInterface) + { + throw new ArgumentException("ContractInterface must be an interface."); + } + } + + /// + /// Generates the WCF service wrapper type. + /// + protected virtual void GenerateProxy() + { + IProxyTypeBuilder builder = new ConfigurableServiceProxyTypeBuilder(TargetName, this.objectName, objectFactory, + Name, Namespace, ConfigurationName, CallbackContract, ProtectionLevel, SessionMode); + if (ContractInterface != null) + { + builder.Interfaces = new Type[] { ContractInterface }; + } + builder.TypeAttributes = TypeAttributes; + builder.MemberAttributes = MemberAttributes; + + proxyType = builder.BuildProxyType(); + } + + #endregion + + #region ConfigurableServiceProxyTypeBuilder inner class implementation + + /// + /// Builds a WCF service type. + /// + private sealed class ConfigurableServiceProxyTypeBuilder : ServiceProxyTypeBuilder + { + private CustomAttributeBuilder serviceContractAttribute; + + public ConfigurableServiceProxyTypeBuilder(string targetName, string objectName, IObjectFactory objectFactory, + string name, string ns, string configurationName, Type callbackContract, ProtectionLevel protectionLevel, SessionMode sessionMode) + : base(targetName, objectName, objectFactory) + { + if (!StringUtils.HasText(configurationName)) + { + name = this.Interfaces[0].Name; + configurationName = this.Interfaces[0].FullName; + } + + // Creates a ServiceContractAttribute from configuration info + this.serviceContractAttribute = CreateServiceContractAttribute(name, ns, configurationName, callbackContract, protectionLevel, sessionMode); + } + + private static CustomAttributeBuilder CreateServiceContractAttribute(string name, string ns, string configurationName, + Type callbackContract, ProtectionLevel protectionLevel, SessionMode sessionMode) + { + ReflectionUtils.CustomAttributeBuilderBuilder scbb = + new ReflectionUtils.CustomAttributeBuilderBuilder(typeof(ServiceContractAttribute)); + if (StringUtils.HasText(name)) + { + scbb.AddPropertyValue("Name", name); + } + if (StringUtils.HasText(ns)) + { + scbb.AddPropertyValue("Namespace", ns); + } + if (StringUtils.HasText(configurationName)) + { + scbb.AddPropertyValue("ConfigurationName", configurationName); + } + if (callbackContract != null) + { + scbb.AddPropertyValue("CallbackContract", callbackContract); + } + if (protectionLevel != ProtectionLevel.None) + { + scbb.AddPropertyValue("ProtectionLevel", protectionLevel); + } + if (sessionMode != SessionMode.Allowed) + { + scbb.AddPropertyValue("SessionMode", sessionMode); + } + return scbb.Build(); + } + + protected override IList GetTypeAttributes(Type type) + { + IList attrs = base.GetTypeAttributes(type); + + bool containsServiceContractAttribute = false; + + for (int i = 0; i < attrs.Count; i++) + { + if (IsAttributeMatchingType(attrs[i], typeof(ServiceContractAttribute))) + { + // Override existing ServiceContractAttribute + containsServiceContractAttribute = true; + attrs[i] = serviceContractAttribute; + } + } + + // Add missing ServiceContractAttribute + if (!containsServiceContractAttribute) + { + attrs.Add(serviceContractAttribute); + } + + return attrs; + } + + protected override IList GetMethodAttributes(MethodInfo method) + { + IList attrs = base.GetMethodAttributes(method); + + bool containsOperationContractAttribute = false; + foreach (object attr in attrs) + { + if (IsAttributeMatchingType(attr, typeof(OperationContractAttribute))) + { + containsOperationContractAttribute = true; + break; + } + } + + // Creates default OperationContractAttribute if not set yet + if (!containsOperationContractAttribute) + { + attrs.Add(ReflectionUtils.CreateCustomAttribute(typeof(OperationContractAttribute))); + } + + return attrs; + } + + protected override Type[] GetProxiableInterfaces(Type[] interfaces) + { + Type[] proxiableInterfaces = base.GetProxiableInterfaces(interfaces); + + if (proxiableInterfaces.Length > 1) + { + throw new ArgumentException(String.Format( + "ServiceExporter cannot export service type '{0}' as a WCF service because it implements multiple interfaces. Specify the contract interface to expose via the ContractInterface property.", + this.TargetType)); + } + + return proxiableInterfaces; + } + } + + #endregion + } +} #endif \ No newline at end of file diff --git a/src/Spring/Spring.Services/ServiceModel/SpringServiceHost.cs b/src/Spring/Spring.Services/ServiceModel/SpringServiceHost.cs index 0d2bd40a..92b82ba3 100644 --- a/src/Spring/Spring.Services/ServiceModel/SpringServiceHost.cs +++ b/src/Spring/Spring.Services/ServiceModel/SpringServiceHost.cs @@ -27,6 +27,7 @@ using Spring.Util; using Spring.Context; using Spring.Context.Support; using Spring.ServiceModel.Support; +using Spring.Objects.Factory; #endregion @@ -65,10 +66,10 @@ namespace Spring.ServiceModel /// Creates a new instance of the class. ///
/// The name of the service within Spring's IoC container. - /// The to use. + /// The to use. /// The base addresses for the hosted service. - public SpringServiceHost(string serviceName, IApplicationContext applicationContext, params Uri[] baseAddresses) - : base(CreateServiceType(serviceName, applicationContext), baseAddresses) + public SpringServiceHost(string serviceName, IObjectFactory objectFactory, params Uri[] baseAddresses) + : base(CreateServiceType(serviceName, objectFactory), baseAddresses) { } @@ -84,16 +85,21 @@ namespace Spring.ServiceModel } } - private static Type CreateServiceType(string serviceName, IApplicationContext applicationContext) + private static Type CreateServiceType(string serviceName, IObjectFactory objectFactory) { if (StringUtils.IsNullOrEmpty(serviceName)) { throw new ArgumentException("The service name cannot be null or an empty string.", "serviceName"); } - Type serviceType = applicationContext.GetType(serviceName); - - return new ServiceProxyTypeBuilder(serviceName, applicationContext, serviceType).BuildProxyType(); + if (objectFactory.IsTypeMatch(serviceName, typeof(Type))) + { + return objectFactory.GetObject(serviceName) as Type; + } + else + { + return new ServiceProxyTypeBuilder(serviceName, objectFactory).BuildProxyType(); + } } #endregion diff --git a/src/Spring/Spring.Services/ServiceModel/Support/ServiceProxyTypeBuilder.cs b/src/Spring/Spring.Services/ServiceModel/Support/ServiceProxyTypeBuilder.cs index 5338d880..48553147 100644 --- a/src/Spring/Spring.Services/ServiceModel/Support/ServiceProxyTypeBuilder.cs +++ b/src/Spring/Spring.Services/ServiceModel/Support/ServiceProxyTypeBuilder.cs @@ -25,9 +25,8 @@ using System; using System.Reflection; using System.Reflection.Emit; -using Spring.Proxy; -using Spring.Context; using Spring.Objects.Factory; +using Spring.Proxy; #endregion @@ -44,13 +43,13 @@ namespace Spring.ServiceModel.Support private static readonly MethodInfo GetObject = typeof(IObjectFactory).GetMethod("GetObject", new Type[] { typeof(string) }); - private IApplicationContext applicationContext; - private string serviceName; + private IObjectFactory objectFactory; + private string targetName; /// /// Target instance calls should be delegated to. /// - protected FieldBuilder applicationContextField; + protected FieldBuilder objectFactoryField; #endregion @@ -60,16 +59,28 @@ namespace Spring.ServiceModel.Support /// Creates a new instance of the /// class. /// - /// The name of the service within Spring's IoC container. - /// The to use. - /// The type of the service. - public ServiceProxyTypeBuilder(string serviceName, IApplicationContext applicationContext, Type serviceType) + /// The name of the service within Spring's IoC container. + /// The to use. + public ServiceProxyTypeBuilder(string targetName, IObjectFactory objectFactory) + : this(targetName, targetName, objectFactory) { - this.serviceName = serviceName; - this.applicationContext = applicationContext; + } - this.Name = serviceName; - this.TargetType = serviceType; + + /// + /// Creates a new instance of the + /// class. + /// + /// The name of the service within Spring's IoC container. + /// The name of the generated WCF service . + /// The to use. + public ServiceProxyTypeBuilder(string targetName, string serviceTypeName, IObjectFactory objectFactory) + { + this.targetName = targetName; + this.objectFactory = objectFactory; + + this.Name = serviceTypeName; + this.TargetType = objectFactory.GetType(targetName); } #endregion @@ -77,8 +88,8 @@ namespace Spring.ServiceModel.Support #region Protected Methods /// - /// Creates a proxy that delegates calls to an instance of the - /// target object. This overriden implementation also sets the field __applicationContext. + /// Creates a proxy that delegates calls to an instance of the target object. + /// This overriden implementation also sets the field __objectFactory. /// /// /// If the @@ -88,8 +99,8 @@ namespace Spring.ServiceModel.Support { Type proxyType = base.BuildProxyType(); - FieldInfo field = proxyType.GetField("__applicationContext", BindingFlags.NonPublic | BindingFlags.Static); - field.SetValue(proxyType, applicationContext); + FieldInfo field = proxyType.GetField("__objectFactory", BindingFlags.NonPublic | BindingFlags.Static); + field.SetValue(proxyType, objectFactory); return proxyType; } @@ -117,8 +128,8 @@ namespace Spring.ServiceModel.Support ILGenerator il = cb.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldsfld, applicationContextField); - il.Emit(OpCodes.Ldstr, serviceName); + il.Emit(OpCodes.Ldsfld, objectFactoryField); + il.Emit(OpCodes.Ldstr, targetName); il.EmitCall(OpCodes.Callvirt, GetObject, null); il.Emit(OpCodes.Stfld, targetInstance); @@ -135,7 +146,7 @@ namespace Spring.ServiceModel.Support { TypeBuilder typeBuilder = DynamicProxyManager.CreateTypeBuilder(name, baseType); - applicationContextField = typeBuilder.DefineField("__applicationContext", typeof(IApplicationContext), + objectFactoryField = typeBuilder.DefineField("__objectFactory", typeof(IObjectFactory), FieldAttributes.Private | FieldAttributes.Static | FieldAttributes.InitOnly); return typeBuilder; diff --git a/test/Spring/Spring.Services.Tests/ServiceModel/ServiceExporterTests.cs b/test/Spring/Spring.Services.Tests/ServiceModel/ServiceExporterTests.cs index 5b033b6e..b2358b45 100644 --- a/test/Spring/Spring.Services.Tests/ServiceModel/ServiceExporterTests.cs +++ b/test/Spring/Spring.Services.Tests/ServiceModel/ServiceExporterTests.cs @@ -22,10 +22,17 @@ #region Imports using System; +using System.IO; +using System.Text; +using System.Reflection; +using System.Net.Security; using System.ServiceModel; using NUnit.Framework; using Spring.ServiceModel; +using Spring.Core.IO; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Xml; #endregion @@ -35,47 +42,10 @@ namespace Spring.ServiceModel /// Unit tests for the ServiceExporter class. /// /// Bruno Baia - /// $Id: ServiceExporterTests.cs,v 1.1 2007/09/21 14:27:35 bbaia Exp $ [TestFixture] public sealed class ServiceExporterTests { - - #region Test classes - - public interface IService - { - string SomeMethod(int param); - } - - [ServiceContract(Namespace = "http://Spring.Services.Tests")] - public interface IDecoratedService - { - [OperationContract] - string SomeMethod(int param); - } - - public class Service : IDecoratedService - { - public string SomeMethod(int param) - { - return param.ToString(); - } - } - - [ServiceContract(Namespace = "http://Spring.Services.Tests")] - public class DecoratedService : IService - { - [OperationContract] - public string SomeMethod(int param) - { - return param.ToString(); - } - } - - #endregion - -/* - WebServiceExporter wse = null; + ServiceExporter se = null; [SetUp] public void SetUp() @@ -83,177 +53,185 @@ namespace Spring.ServiceModel const string xml = @" - - + + "; Stream stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)); IObjectFactory objectFactory = new XmlObjectFactory(new InputStreamResource(stream, string.Empty)); - wse = new WebServiceExporter(); - wse.ObjectFactory = objectFactory; + se = new ServiceExporter(); + se.ObjectFactory = objectFactory; } [Test] - [ExpectedException(typeof(ArgumentException), "The TargetName property is required.")] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = "The TargetName property is required.")] public void NullConfig() { - wse.ObjectName = "NullConfig"; - wse.AfterPropertiesSet(); + se.ObjectName = "NullConfig"; + se.AfterPropertiesSet(); } [Test] - public void ProxiesTargetInterfaces() + public void ProxiesContractInterface() { - wse.ObjectName = "ProxiesTargetInterfaces"; - wse.TargetName = "noDecoratedService"; - wse.AfterPropertiesSet(); + se.ObjectName = "ProxiesContractInterface"; + se.TargetName = "service"; + se.AfterPropertiesSet(); - Type proxyType = wse.ObjectType; - Assert.IsTrue(typeof(IService).IsAssignableFrom(proxyType)); + Type proxyType = se.GetObject() as Type; + Assert.IsNotNull(proxyType); + Assert.IsTrue(typeof(IContract).IsAssignableFrom(proxyType)); } [Test] - public void CreatesWebServiceAttributeWithNoDecoratedClassAndMinimalConfig() + public void ProxyTypeEqualsObjectName() { - wse.ObjectName = "CreatesWebServiceAttributeWithNoDecoratedClassAndMinimalConfig"; - wse.TargetName = "noDecoratedService"; - wse.AfterPropertiesSet(); + se.ObjectName = "ProxyTypeEqualsObjectName"; + se.TargetName = "service"; + se.AfterPropertiesSet(); - Type proxyType = wse.ObjectType; - object[] attrs = proxyType.GetCustomAttributes(typeof(WebServiceAttribute), true); + Type proxyType = se.GetObject() as Type; + Assert.IsNotNull(proxyType); + Assert.AreEqual("ProxyTypeEqualsObjectName", proxyType.FullName); + } + + [Test] + public void CreatesServiceContractAttributeWithNoDecoratedClassAndMinimalConfig() + { + se.ObjectName = "CreatesServiceContractAttributeWithNoDecoratedClassAndMinimalConfig"; + se.TargetName = "service"; + se.AfterPropertiesSet(); + + Type proxyType = se.GetObject() as Type; + Assert.IsNotNull(proxyType); + object[] attrs = proxyType.GetCustomAttributes(typeof(ServiceContractAttribute), true); Assert.IsNotEmpty(attrs); Assert.AreEqual(1, attrs.Length); - WebServiceAttribute wsa = attrs[0] as WebServiceAttribute; - Assert.AreEqual("CreatesWebServiceAttributeWithNoDecoratedClassAndMinimalConfig", wsa.Name); - Assert.AreEqual(string.Empty, wsa.Description); - Assert.AreEqual(WebServiceAttribute.DefaultNamespace, wsa.Namespace); + ServiceContractAttribute sca = attrs[0] as ServiceContractAttribute; + Assert.IsNull(sca.CallbackContract); + Assert.AreEqual(typeof(IContract).FullName, sca.ConfigurationName); + Assert.AreEqual(typeof(IContract).Name, sca.Name); + Assert.IsNull(sca.Namespace); + Assert.AreEqual(ProtectionLevel.None, sca.ProtectionLevel); + Assert.AreEqual(SessionMode.Allowed, sca.SessionMode); } [Test] - public void CreatesWebServiceAttributeWithNoDecoratedClassAndFullConfig() + public void CreatesServiceContractAttributeWithNoDecoratedClassAndFullConfig() { - wse.ObjectName = "CreatesWebServiceAttributeWithNoDecoratedClassAndFullConfig"; - wse.TargetName = "noDecoratedService"; - wse.Name = "My web service name"; - wse.Description = "My web service description"; - wse.Namespace = "http://www.springframework.net"; - wse.AfterPropertiesSet(); + se.ObjectName = "CreatesServiceContractAttributeWithNoDecoratedClassAndFullConfig"; + se.TargetName = "service"; + se.CallbackContract = typeof(IDisposable); + se.ConfigurationName = "CustomConfigName"; + se.Name = "serviceName"; + se.Namespace = "http://Spring.Services.Tests"; + se.ProtectionLevel = ProtectionLevel.Sign; + se.SessionMode = SessionMode.Required; + + se.AfterPropertiesSet(); - Type proxyType = wse.ObjectType; - object[] attrs = proxyType.GetCustomAttributes(typeof(WebServiceAttribute), true); + Type proxyType = se.GetObject() as Type; + Assert.IsNotNull(proxyType); + object[] attrs = proxyType.GetCustomAttributes(typeof(ServiceContractAttribute), true); Assert.IsNotEmpty(attrs); Assert.AreEqual(1, attrs.Length); - WebServiceAttribute wsa = attrs[0] as WebServiceAttribute; - Assert.AreEqual(wse.Name, wsa.Name); - Assert.AreEqual(wse.Description, wsa.Description); - Assert.AreEqual(wse.Namespace, wsa.Namespace); + ServiceContractAttribute sca = attrs[0] as ServiceContractAttribute; + Assert.AreEqual(se.CallbackContract, sca.CallbackContract); + Assert.AreEqual(se.ConfigurationName, sca.ConfigurationName); + Assert.AreEqual(se.Name, sca.Name); + Assert.AreEqual(se.Namespace, sca.Namespace); + Assert.AreEqual(se.ProtectionLevel, sca.ProtectionLevel); + Assert.AreEqual(se.SessionMode, sca.SessionMode); } [Test] - public void CreatesDefaultWebMethodAttributeWithNoDecoratedMethod() + public void CreatesDefaultOperationContractAttributeWithNoDecoratedMethod() { - wse.ObjectName = "CreatesDefaultWebMethodAttributeWithNoDecoratedMethod"; - wse.TargetName = "noDecoratedService"; - wse.AfterPropertiesSet(); + se.ObjectName = "CreatesDefaultOperationContractAttributeWithNoDecoratedMethod"; + se.TargetName = "service"; + se.AfterPropertiesSet(); - Type proxyType = wse.ObjectType; + Type proxyType = se.GetObject() as Type; + Assert.IsNotNull(proxyType); MethodInfo method = proxyType.GetMethod("SomeMethod"); Assert.IsNotNull(method); - object[] attrs = method.GetCustomAttributes(typeof(WebMethodAttribute), true); + object[] attrs = method.GetCustomAttributes(typeof(OperationContractAttribute), true); Assert.IsNotEmpty(attrs); Assert.AreEqual(1, attrs.Length); } [Test] - public void CreatesCustomWebMethodAttributeWithNoDecoratedMethod() + public void CreatesCustomOperationContractAttributeWithNoDecoratedMethod() { - wse.ObjectName = "CreatesCustomWebMethodAttributeWithNoDecoratedMethod"; - wse.TargetName = "noDecoratedService"; - wse.MemberAttributes.Add("SomeMethod", new WebMethodAttribute(true)); // default value is false - wse.AfterPropertiesSet(); + OperationContractAttribute oca1 = new OperationContractAttribute(); + oca1.Name = "MySomeMethod"; - Type proxyType = wse.ObjectType; + se.ObjectName = "CreatesCustomOperationContractAttributeWithNoDecoratedMethod"; + se.TargetName = "service"; + se.MemberAttributes.Add("SomeMethod", oca1); + se.AfterPropertiesSet(); + + Type proxyType = se.GetObject() as Type; + Assert.IsNotNull(proxyType); MethodInfo method = proxyType.GetMethod("SomeMethod"); Assert.IsNotNull(method); - object[] attrs = method.GetCustomAttributes(typeof(WebMethodAttribute), true); + object[] attrs = method.GetCustomAttributes(typeof(OperationContractAttribute), true); Assert.IsNotEmpty(attrs); Assert.AreEqual(1, attrs.Length); - WebMethodAttribute wma = attrs[0] as WebMethodAttribute; - Assert.AreEqual(true, wma.EnableSession); + OperationContractAttribute oca2 = attrs[0] as OperationContractAttribute; + Assert.AreEqual(oca1.Name, oca2.Name); } [Test] - public void OverridesExistingWebServiceAttributeWithDecoratedClass() + public void OverridesExistingServiceContractAttributeWithDecoratedClass() { - wse.ObjectName = "OverridesExistingWebServiceAttributeWithDecoratedClass"; - wse.TargetName = "decoratedService"; - wse.AfterPropertiesSet(); + se.ObjectName = "OverridesExistingServiceContractAttributeWithDecoratedClass"; + se.TargetName = "decoratedService"; + se.AfterPropertiesSet(); - Type proxyType = wse.ObjectType; - object[] attrs = proxyType.GetCustomAttributes(typeof(WebServiceAttribute), true); + Type proxyType = se.GetObject() as Type; + Assert.IsNotNull(proxyType); + object[] attrs = proxyType.GetCustomAttributes(typeof(ServiceContractAttribute), true); Assert.IsNotEmpty(attrs); Assert.AreEqual(1, attrs.Length); - WebServiceAttribute wsa = attrs[0] as WebServiceAttribute; - Assert.AreEqual("OverridesExistingWebServiceAttributeWithDecoratedClass", wsa.Name); - Assert.AreEqual(string.Empty, wsa.Description); - Assert.AreEqual(WebServiceAttribute.DefaultNamespace, wsa.Namespace); + ServiceContractAttribute sca = attrs[0] as ServiceContractAttribute; + Assert.IsNull(sca.Namespace); } [Test] - public void UsesExistingWebMethodAttributeWithDecoratedMethod() + public void UsesExistingOperationContractAttributeWithDecoratedMethod() { - wse.ObjectName = "UsesExistingWebMethodAttributeWithDecoratedMethod"; - wse.TargetName = "decoratedService"; - wse.AfterPropertiesSet(); + se.ObjectName = "UsesExistingOperationContractAttributeWithDecoratedMethod"; + se.TargetName = "decoratedService"; + se.AfterPropertiesSet(); - Type proxyType = wse.ObjectType; + Type proxyType = se.GetObject() as Type; + Assert.IsNotNull(proxyType); MethodInfo method = proxyType.GetMethod("SomeMethod"); Assert.IsNotNull(method); - object[] attrs = method.GetCustomAttributes(typeof(WebMethodAttribute), true); + object[] attrs = method.GetCustomAttributes(typeof(OperationContractAttribute), true); Assert.IsNotEmpty(attrs); Assert.AreEqual(1, attrs.Length); - WebMethodAttribute wma = attrs[0] as WebMethodAttribute; - Assert.AreEqual("SomeMethod description", wma.Description); - } - - // TODO : attributes override - [Test] - [Ignore("Attributes override not implemented.")] - public void OverridesExistingWebMethodAttributeWithDecoratedMethod() - { - wse.ObjectName = "OverridesExistingWebMethodAttributeWithDecoratedMethod"; - wse.TargetName = "decoratedService"; - wse.MemberAttributes.Add("SomeMethod", new WebMethodAttribute(true)); // default value is false - wse.AfterPropertiesSet(); - - Type proxyType = wse.ObjectType; - MethodInfo method = proxyType.GetMethod("SomeMethod"); - Assert.IsNotNull(method); - - object[] attrs = method.GetCustomAttributes(typeof(WebMethodAttribute), true); - Assert.IsNotEmpty(attrs); - Assert.AreEqual(1, attrs.Length); - - WebMethodAttribute wma = attrs[0] as WebMethodAttribute; - Assert.AreEqual(true, wma.EnableSession); + OperationContractAttribute oca = attrs[0] as OperationContractAttribute; + Assert.AreEqual("MySomeMethod", oca.Name); } #region Test classes - public interface IService + public interface IContract { string SomeMethod(int param); } - public class NoDecoratedService : IService + public class Service : IContract { public string SomeMethod(int param) { @@ -261,18 +239,33 @@ namespace Spring.ServiceModel } } - [WebService(Name = "Decorated service")] - public class DecoratedService : IService + [ServiceContract(Namespace = "http://Spring.Services.Tests")] + public class DecoratedService : IContract { - [WebMethod(Description="SomeMethod description")] + [OperationContract(Name = "MySomeMethod")] public string SomeMethod(int param) { return param.ToString(); } } + //[ServiceContract(Namespace = "http://Spring.Services.Tests")] + //public interface IDecoratedContract + //{ + // [OperationContract] + // string SomeMethod(int param); + //} + + //public class AnotherService : IDecoratedContract + //{ + // public string SomeMethod(int param) + // { + // return param.ToString(); + // } + //} + #endregion -*/ + } } #endif \ No newline at end of file