//----------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- namespace System.ServiceModel.Description { using System.Collections; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Runtime; using System.ServiceModel; using System.ServiceModel.Channels; using System.Xml; using System.Xml.Schema; using System.Xml.Serialization; using WsdlNS = System.Web.Services.Description; public class WsdlExporter : MetadataExporter { static XmlDocument xmlDocument; bool isFaulted = false; WsdlNS.ServiceDescriptionCollection wsdlDocuments = new WsdlNS.ServiceDescriptionCollection(); XmlSchemaSet xmlSchemas = WsdlExporter.GetEmptySchemaSet(); Dictionary exportedContracts = new Dictionary(); Dictionary exportedBindings = new Dictionary(); Dictionary exportedEndpoints = new Dictionary(); public override void ExportContract(ContractDescription contract) { if (this.isFaulted) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.WsdlExporterIsFaulted))); if (contract == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("contract"); if (!this.exportedContracts.ContainsKey(contract)) { try { WsdlNS.PortType wsdlPortType = CreateWsdlPortType(contract); WsdlContractConversionContext contractContext; contractContext = new WsdlContractConversionContext(contract, wsdlPortType); foreach (OperationDescription operation in contract.Operations) { bool isWildcardAction; if (!OperationIsExportable(operation, out isWildcardAction)) { string warningMsg = isWildcardAction ? SR.GetString(SR.WarnSkippingOpertationWithWildcardAction, contract.Name, contract.Namespace, operation.Name) : SR.GetString(SR.WarnSkippingOpertationWithSessionOpenNotificationEnabled, "Action", OperationDescription.SessionOpenedAction, contract.Name, contract.Namespace, operation.Name); LogExportWarning(warningMsg); continue; } WsdlNS.Operation wsdlOperation = CreateWsdlOperation(operation, contract); wsdlPortType.Operations.Add(wsdlOperation); contractContext.AddOperation(operation, wsdlOperation); foreach (MessageDescription message in operation.Messages) { //Create Operation Message WsdlNS.OperationMessage wsdlOperationMessage = CreateWsdlOperationMessage(message); wsdlOperation.Messages.Add(wsdlOperationMessage); contractContext.AddMessage(message, wsdlOperationMessage); } foreach (FaultDescription fault in operation.Faults) { //Create Operation Fault WsdlNS.OperationFault wsdlOperationFault = CreateWsdlOperationFault(fault); wsdlOperation.Faults.Add(wsdlOperationFault); contractContext.AddFault(fault, wsdlOperationFault); } } CallExportContract(contractContext); exportedContracts.Add(contract, contractContext); } catch { isFaulted = true; throw; } } } public override void ExportEndpoint(ServiceEndpoint endpoint) { if (this.isFaulted) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.WsdlExporterIsFaulted))); if (endpoint == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpoint"); ExportEndpoint(endpoint, new XmlQualifiedName(NamingHelper.DefaultServiceName, NamingHelper.DefaultNamespace), null); } public void ExportEndpoints(IEnumerable endpoints, XmlQualifiedName wsdlServiceQName) { this.ExportEndpoints(endpoints, wsdlServiceQName, null); } internal void ExportEndpoints(IEnumerable endpoints, XmlQualifiedName wsdlServiceQName, BindingParameterCollection bindingParameters) { if (this.isFaulted) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.WsdlExporterIsFaulted))); if (endpoints == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpoints"); if (wsdlServiceQName == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("wsdlServiceQName"); foreach (ServiceEndpoint endpoint in endpoints) { ExportEndpoint(endpoint, wsdlServiceQName, bindingParameters); } } public override MetadataSet GetGeneratedMetadata() { MetadataSet set = new MetadataSet(); foreach (WsdlNS.ServiceDescription wsdl in wsdlDocuments) set.MetadataSections.Add(MetadataSection.CreateFromServiceDescription(wsdl)); foreach (XmlSchema schema in xmlSchemas.Schemas()) set.MetadataSections.Add(MetadataSection.CreateFromSchema(schema)); return set; } public WsdlNS.ServiceDescriptionCollection GeneratedWsdlDocuments { get { return wsdlDocuments; } } public XmlSchemaSet GeneratedXmlSchemas { get { return xmlSchemas; } } void ExportEndpoint(ServiceEndpoint endpoint, XmlQualifiedName wsdlServiceQName, BindingParameterCollection bindingParameters) { if (endpoint.Binding == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.EndpointsMustHaveAValidBinding1, endpoint.Name))); EndpointDictionaryKey endpointKey = new EndpointDictionaryKey(endpoint, wsdlServiceQName); try { if (exportedEndpoints.ContainsKey(endpointKey)) return; this.ExportContract(endpoint.Contract); // Retreive Conversion Context for Contract; // Note: Contract must have already been exported at this point. WsdlContractConversionContext contractContext = this.exportedContracts[endpoint.Contract]; bool newWsdlBinding, bindingNameWasUniquified; WsdlNS.Port wsdlPort; WsdlNS.Binding wsdlBinding; wsdlBinding = CreateWsdlBindingAndPort(endpoint, wsdlServiceQName, out wsdlPort, out newWsdlBinding, out bindingNameWasUniquified); if (!newWsdlBinding && wsdlPort == null) return; // Create an Endpoint conversion context based on // the contract's conversion context (reuse contract correlation information) WsdlEndpointConversionContext endpointContext; if (newWsdlBinding) { endpointContext = new WsdlEndpointConversionContext(contractContext, endpoint, wsdlBinding, wsdlPort); foreach (OperationDescription operation in endpoint.Contract.Operations) { if (!WsdlExporter.OperationIsExportable(operation)) { continue; } WsdlNS.OperationBinding wsdlOperationBinding = CreateWsdlOperationBinding(endpoint.Contract, operation); wsdlBinding.Operations.Add(wsdlOperationBinding); endpointContext.AddOperationBinding(operation, wsdlOperationBinding); foreach (MessageDescription message in operation.Messages) { WsdlNS.MessageBinding wsdlMessageBinding = CreateWsdlMessageBinding(message, endpoint.Binding, wsdlOperationBinding); endpointContext.AddMessageBinding(message, wsdlMessageBinding); } foreach (FaultDescription fault in operation.Faults) { WsdlNS.FaultBinding wsdlFaultBinding = CreateWsdlFaultBinding(fault, endpoint.Binding, wsdlOperationBinding); endpointContext.AddFaultBinding(fault, wsdlFaultBinding); } } // CSDMain 180381: Added internal functionality for passing BindingParameters into the ExportPolicy process via PolicyConversionContext. // However, in order to not change existing behavior, we only call the internal ExportPolicy method which accepts BindingParameters if they are not null // (non-null binding parameters can only be passed in via internal code paths). Otherwise, we call the existing ExportPolicy method, just like before. PolicyConversionContext policyContext; if (bindingParameters == null) { policyContext = this.ExportPolicy(endpoint); } else { policyContext = this.ExportPolicy(endpoint, bindingParameters); } // consider factoring this out of wsdl exporter new WSPolicyAttachmentHelper(this.PolicyVersion).AttachPolicy(endpoint, endpointContext, policyContext); exportedBindings.Add(new BindingDictionaryKey(endpoint.Contract, endpoint.Binding), endpointContext); } else { endpointContext = new WsdlEndpointConversionContext(exportedBindings[new BindingDictionaryKey(endpoint.Contract, endpoint.Binding)], endpoint, wsdlPort); } CallExportEndpoint(endpointContext); exportedEndpoints.Add(endpointKey, endpoint); if (bindingNameWasUniquified) Errors.Add(new MetadataConversionError(SR.GetString(SR.WarnDuplicateBindingQNameNameOnExport, endpoint.Binding.Name, endpoint.Binding.Namespace, endpoint.Contract.Name), true /*isWarning*/)); } catch { isFaulted = true; throw; } } void CallExportEndpoint(WsdlEndpointConversionContext endpointContext) { foreach (IWsdlExportExtension extension in endpointContext.ExportExtensions) { CallExtension(endpointContext, extension); } } void CallExportContract(WsdlContractConversionContext contractContext) { foreach (IWsdlExportExtension extension in contractContext.ExportExtensions) { CallExtension(contractContext, extension); } } WsdlNS.PortType CreateWsdlPortType(ContractDescription contract) { XmlQualifiedName wsdlPortTypeQName = WsdlNamingHelper.GetPortTypeQName(contract); WsdlNS.ServiceDescription wsdl = GetOrCreateWsdl(wsdlPortTypeQName.Namespace); WsdlNS.PortType wsdlPortType = new WsdlNS.PortType(); wsdlPortType.Name = wsdlPortTypeQName.Name; if (wsdl.PortTypes[wsdlPortType.Name] != null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.DuplicateContractQNameNameOnExport, contract.Name, contract.Namespace))); NetSessionHelper.AddUsingSessionAttributeIfNeeded(wsdlPortType, contract); wsdl.PortTypes.Add(wsdlPortType); return wsdlPortType; } WsdlNS.Operation CreateWsdlOperation(OperationDescription operation, ContractDescription contract) { WsdlNS.Operation wsdlOperation = new WsdlNS.Operation(); wsdlOperation.Name = WsdlNamingHelper.GetWsdlOperationName(operation, contract); NetSessionHelper.AddInitiatingTerminatingAttributesIfNeeded(wsdlOperation, operation, contract); return wsdlOperation; } WsdlNS.OperationMessage CreateWsdlOperationMessage(MessageDescription message) { WsdlNS.OperationMessage wsdlOperationMessage; if (message.Direction == MessageDirection.Input) wsdlOperationMessage = new WsdlNS.OperationInput(); else wsdlOperationMessage = new WsdlNS.OperationOutput(); if (!XmlName.IsNullOrEmpty(message.MessageName)) wsdlOperationMessage.Name = message.MessageName.EncodedName; // consider factoring this out of wslExporter WSAddressingHelper.AddActionAttribute(message.Action, wsdlOperationMessage, this.PolicyVersion); return wsdlOperationMessage; } WsdlNS.OperationFault CreateWsdlOperationFault(FaultDescription fault) { WsdlNS.OperationFault wsdlOperationFault; wsdlOperationFault = new WsdlNS.OperationFault(); // operation fault name must not be empty (FaultDescription checks this) wsdlOperationFault.Name = fault.Name; // consider factoring this out of wslExporter WSAddressingHelper.AddActionAttribute(fault.Action, wsdlOperationFault, this.PolicyVersion); return wsdlOperationFault; } WsdlNS.Binding CreateWsdlBindingAndPort(ServiceEndpoint endpoint, XmlQualifiedName wsdlServiceQName, out WsdlNS.Port wsdlPort, out bool newBinding, out bool bindingNameWasUniquified) { WsdlNS.ServiceDescription bindingWsdl; WsdlNS.Binding wsdlBinding; WsdlEndpointConversionContext bindingConversionContext; XmlQualifiedName wsdlBindingQName; XmlQualifiedName wsdlPortTypeQName; bool printWsdlDeclaration = IsWsdlExportable(endpoint.Binding); if (!exportedBindings.TryGetValue(new BindingDictionaryKey(endpoint.Contract, endpoint.Binding), out bindingConversionContext)) { wsdlBindingQName = WsdlNamingHelper.GetBindingQName(endpoint, this, out bindingNameWasUniquified); bindingWsdl = GetOrCreateWsdl(wsdlBindingQName.Namespace); wsdlBinding = new WsdlNS.Binding(); wsdlBinding.Name = wsdlBindingQName.Name; newBinding = true; WsdlNS.PortType wsdlPortType = exportedContracts[endpoint.Contract].WsdlPortType; wsdlPortTypeQName = new XmlQualifiedName(wsdlPortType.Name, wsdlPortType.ServiceDescription.TargetNamespace); wsdlBinding.Type = wsdlPortTypeQName; if (printWsdlDeclaration) { bindingWsdl.Bindings.Add(wsdlBinding); } WsdlExporter.EnsureWsdlContainsImport(bindingWsdl, wsdlPortTypeQName.Namespace); } else { wsdlBindingQName = new XmlQualifiedName(bindingConversionContext.WsdlBinding.Name, bindingConversionContext.WsdlBinding.ServiceDescription.TargetNamespace); bindingNameWasUniquified = false; bindingWsdl = wsdlDocuments[wsdlBindingQName.Namespace]; wsdlBinding = bindingWsdl.Bindings[wsdlBindingQName.Name]; wsdlPortTypeQName = wsdlBinding.Type; newBinding = false; } //We can only create a Port if there is an address if (endpoint.Address != null) { WsdlNS.Service wsdlService = GetOrCreateWsdlService(wsdlServiceQName); wsdlPort = new WsdlNS.Port(); string wsdlPortName = WsdlNamingHelper.GetPortName(endpoint, wsdlService); wsdlPort.Name = wsdlPortName; wsdlPort.Binding = wsdlBindingQName; WsdlNS.SoapAddressBinding addressBinding = SoapHelper.GetOrCreateSoapAddressBinding(wsdlBinding, wsdlPort, this); if (addressBinding != null) { addressBinding.Location = endpoint.Address.Uri.AbsoluteUri; } WsdlExporter.EnsureWsdlContainsImport(wsdlService.ServiceDescription, wsdlBindingQName.Namespace); if (printWsdlDeclaration) { wsdlService.Ports.Add(wsdlPort); } } else { wsdlPort = null; } return wsdlBinding; } WsdlNS.OperationBinding CreateWsdlOperationBinding(ContractDescription contract, OperationDescription operation) { WsdlNS.OperationBinding wsdlOperationBinding = new WsdlNS.OperationBinding(); wsdlOperationBinding.Name = WsdlNamingHelper.GetWsdlOperationName(operation, contract); return wsdlOperationBinding; } WsdlNS.MessageBinding CreateWsdlMessageBinding(MessageDescription messageDescription, Binding binding, WsdlNS.OperationBinding wsdlOperationBinding) { WsdlNS.MessageBinding wsdlMessageBinding; if (messageDescription.Direction == MessageDirection.Input) { wsdlOperationBinding.Input = new WsdlNS.InputBinding(); wsdlMessageBinding = wsdlOperationBinding.Input; } else { wsdlOperationBinding.Output = new WsdlNS.OutputBinding(); wsdlMessageBinding = wsdlOperationBinding.Output; } if (!XmlName.IsNullOrEmpty(messageDescription.MessageName)) wsdlMessageBinding.Name = messageDescription.MessageName.EncodedName; return wsdlMessageBinding; } WsdlNS.FaultBinding CreateWsdlFaultBinding(FaultDescription faultDescription, Binding binding, WsdlNS.OperationBinding wsdlOperationBinding) { WsdlNS.FaultBinding wsdlFaultBinding = new WsdlNS.FaultBinding(); wsdlOperationBinding.Faults.Add(wsdlFaultBinding); if (faultDescription.Name != null) wsdlFaultBinding.Name = faultDescription.Name; return wsdlFaultBinding; } internal static bool OperationIsExportable(OperationDescription operation) { bool isWildcardAction; return OperationIsExportable(operation, out isWildcardAction); } internal static bool OperationIsExportable(OperationDescription operation, out bool isWildcardAction) { isWildcardAction = false; if (operation.IsSessionOpenNotificationEnabled) { return false; } for (int i = 0; i < operation.Messages.Count; i++) { if (operation.Messages[i].Action == MessageHeaders.WildcardAction) { isWildcardAction = true; return false; } } return true; } internal static bool IsBuiltInOperationBehavior(IWsdlExportExtension extension) { DataContractSerializerOperationBehavior dcsob = extension as DataContractSerializerOperationBehavior; if (dcsob != null) { return dcsob.IsBuiltInOperationBehavior; } XmlSerializerOperationBehavior xsob = extension as XmlSerializerOperationBehavior; if (xsob != null) { return xsob.IsBuiltInOperationBehavior; } return false; } static XmlDocument XmlDoc { get { if (xmlDocument == null) { NameTable nameTable = new NameTable(); nameTable.Add(MetadataStrings.WSPolicy.Elements.Policy); nameTable.Add(MetadataStrings.WSPolicy.Elements.All); nameTable.Add(MetadataStrings.WSPolicy.Elements.ExactlyOne); nameTable.Add(MetadataStrings.WSPolicy.Attributes.PolicyURIs); nameTable.Add(MetadataStrings.Wsu.Attributes.Id); nameTable.Add(MetadataStrings.Addressing200408.Policy.UsingAddressing); nameTable.Add(MetadataStrings.Addressing10.WsdlBindingPolicy.UsingAddressing); nameTable.Add(MetadataStrings.Addressing10.MetadataPolicy.Addressing); nameTable.Add(MetadataStrings.Addressing10.MetadataPolicy.AnonymousResponses); nameTable.Add(MetadataStrings.Addressing10.MetadataPolicy.NonAnonymousResponses); xmlDocument = new XmlDocument(nameTable); } return xmlDocument; } } // Generate WSDL Document if it doesn't already exist otherwise, return the appropriate WSDL document internal WsdlNS.ServiceDescription GetOrCreateWsdl(string ns) { // NOTE: this method is not thread safe WsdlNS.ServiceDescriptionCollection wsdlCollection = this.wsdlDocuments; WsdlNS.ServiceDescription wsdl = wsdlCollection[ns]; // Look for wsdl in service descriptions that have been created. If we cannot find it then we create it if (wsdl == null) { wsdl = new WsdlNS.ServiceDescription(); wsdl.TargetNamespace = ns; XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces(new WsdlNamespaceHelper(this.PolicyVersion).SerializerNamespaces); if (!string.IsNullOrEmpty(wsdl.TargetNamespace)) namespaces.Add("tns", wsdl.TargetNamespace); wsdl.Namespaces = namespaces; wsdlCollection.Add(wsdl); } return wsdl; } WsdlNS.Service GetOrCreateWsdlService(XmlQualifiedName wsdlServiceQName) { // NOTE: this method is not thread safe WsdlNS.ServiceDescription wsdl = GetOrCreateWsdl(wsdlServiceQName.Namespace); WsdlNS.Service wsdlService = wsdl.Services[wsdlServiceQName.Name]; if (wsdlService == null) { //Service not found. Create service. wsdlService = new WsdlNS.Service(); wsdlService.Name = wsdlServiceQName.Name; if (string.IsNullOrEmpty(wsdl.Name)) wsdl.Name = wsdlService.Name; wsdl.Services.Add(wsdlService); } return wsdlService; } static void EnsureWsdlContainsImport(WsdlNS.ServiceDescription srcWsdl, string target) { if (srcWsdl.TargetNamespace == target) return; // FindImport foreach (WsdlNS.Import import in srcWsdl.Imports) { if (import.Namespace == target) return; } { WsdlNS.Import import = new WsdlNS.Import(); import.Location = null; import.Namespace = target; srcWsdl.Imports.Add(import); WsdlNamespaceHelper.FindOrCreatePrefix("i", target, srcWsdl); return; } } void LogExportWarning(string warningMessage) { this.Errors.Add(new MetadataConversionError(warningMessage, true)); } static internal XmlSchemaSet GetEmptySchemaSet() { XmlSchemaSet schemaSet = new XmlSchemaSet(); schemaSet.XmlResolver = null; return schemaSet; } static bool IsWsdlExportable(Binding binding) { BindingElementCollection bindingElements = binding.CreateBindingElements(); if (bindingElements == null) { return true; } foreach (BindingElement bindingElement in bindingElements) { MessageEncodingBindingElement messageEncodingBindingElement = bindingElement as MessageEncodingBindingElement; if (messageEncodingBindingElement != null && !messageEncodingBindingElement.IsWsdlExportable) { return false; } } return true; } internal static class WSAddressingHelper { internal static void AddActionAttribute(string actionUri, WsdlNS.OperationMessage wsdlOperationMessage, PolicyVersion policyVersion) { XmlAttribute attribute; if (policyVersion == PolicyVersion.Policy12) { attribute = WsdlExporter.XmlDoc.CreateAttribute(MetadataStrings.AddressingWsdl.Prefix, MetadataStrings.AddressingWsdl.Action, MetadataStrings.AddressingWsdl.NamespaceUri); } else { attribute = WsdlExporter.XmlDoc.CreateAttribute(MetadataStrings.AddressingMetadata.Prefix, MetadataStrings.AddressingMetadata.Action, MetadataStrings.AddressingMetadata.NamespaceUri); } attribute.Value = actionUri; wsdlOperationMessage.ExtensibleAttributes = new XmlAttribute[] { attribute }; } internal static void AddAddressToWsdlPort(WsdlNS.Port wsdlPort, EndpointAddress addr, AddressingVersion addressing) { if (addressing == AddressingVersion.None) { return; } MemoryStream stream = new MemoryStream(); XmlWriter xw = XmlWriter.Create(stream); xw.WriteStartElement("temp"); if (addressing == AddressingVersion.WSAddressing10) { xw.WriteAttributeString("xmlns", MetadataStrings.Addressing10.Prefix, null, MetadataStrings.Addressing10.NamespaceUri); } else if (addressing == AddressingVersion.WSAddressingAugust2004) { xw.WriteAttributeString("xmlns", MetadataStrings.Addressing200408.Prefix, null, MetadataStrings.Addressing200408.NamespaceUri); } else { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new InvalidOperationException(SR.GetString(SR.AddressingVersionNotSupported, addressing))); } addr.WriteTo(addressing, xw); xw.WriteEndElement(); xw.Flush(); stream.Seek(0, SeekOrigin.Begin); XmlReader xr = XmlReader.Create(stream); xr.MoveToContent(); XmlElement endpointRef = (XmlElement)XmlDoc.ReadNode(xr).ChildNodes[0]; wsdlPort.Extensions.Add(endpointRef); } internal static void AddWSAddressingAssertion(MetadataExporter exporter, PolicyConversionContext context, AddressingVersion addressVersion) { XmlElement addressingAssertion; if (addressVersion == AddressingVersion.WSAddressingAugust2004) { addressingAssertion = XmlDoc.CreateElement(MetadataStrings.Addressing200408.Policy.Prefix, MetadataStrings.Addressing200408.Policy.UsingAddressing, MetadataStrings.Addressing200408.Policy.NamespaceUri); } else if (addressVersion == AddressingVersion.WSAddressing10) { if (exporter.PolicyVersion == PolicyVersion.Policy12) { addressingAssertion = XmlDoc.CreateElement(MetadataStrings.Addressing10.WsdlBindingPolicy.Prefix, MetadataStrings.Addressing10.WsdlBindingPolicy.UsingAddressing, MetadataStrings.Addressing10.WsdlBindingPolicy.NamespaceUri); } else { addressingAssertion = XmlDoc.CreateElement(MetadataStrings.Addressing10.MetadataPolicy.Prefix, MetadataStrings.Addressing10.MetadataPolicy.Addressing, MetadataStrings.Addressing10.MetadataPolicy.NamespaceUri); // All of our existing transports are anonymous, so default to it. SupportedAddressingMode mode = SupportedAddressingMode.Anonymous; string key = typeof(SupportedAddressingMode).Name; if (exporter.State.ContainsKey(key) && exporter.State[key] is SupportedAddressingMode) { mode = (SupportedAddressingMode)exporter.State[key]; if (!SupportedAddressingModeHelper.IsDefined(mode)) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SupportedAddressingModeNotSupported, mode))); } if (mode != SupportedAddressingMode.Mixed) { string responsesAssertionLocalName; if (mode == SupportedAddressingMode.Anonymous) { responsesAssertionLocalName = MetadataStrings.Addressing10.MetadataPolicy.AnonymousResponses; } else { responsesAssertionLocalName = MetadataStrings.Addressing10.MetadataPolicy.NonAnonymousResponses; } XmlElement innerPolicyElement = XmlDoc.CreateElement(MetadataStrings.WSPolicy.Prefix, MetadataStrings.WSPolicy.Elements.Policy, MetadataStrings.WSPolicy.NamespaceUri15); XmlElement responsesAssertion = XmlDoc.CreateElement(MetadataStrings.Addressing10.MetadataPolicy.Prefix, responsesAssertionLocalName, MetadataStrings.Addressing10.MetadataPolicy.NamespaceUri); innerPolicyElement.AppendChild(responsesAssertion); addressingAssertion.AppendChild(innerPolicyElement); } } } else if (addressVersion == AddressingVersion.None) { // do nothing addressingAssertion = null; } else { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new InvalidOperationException(SR.GetString(SR.AddressingVersionNotSupported, addressVersion))); } if (addressingAssertion != null) { context.GetBindingAssertions().Add(addressingAssertion); } } } class WSPolicyAttachmentHelper { PolicyVersion policyVersion; internal WSPolicyAttachmentHelper(PolicyVersion policyVersion) { this.policyVersion = policyVersion; } internal void AttachPolicy(ServiceEndpoint endpoint, WsdlEndpointConversionContext endpointContext, PolicyConversionContext policyContext) { SortedList policyKeys = new SortedList(); NamingHelper.DoesNameExist policyKeyIsUnique = delegate(string name, object nameCollection) { return policyKeys.ContainsKey(name); }; string key, keyBase; ICollection assertions; WsdlNS.ServiceDescription policyWsdl = endpointContext.WsdlBinding.ServiceDescription; assertions = policyContext.GetBindingAssertions(); // Add [wsdl:Binding] level Policy WsdlNS.Binding wsdlBinding = endpointContext.WsdlBinding; if (assertions.Count > 0) { keyBase = CreateBindingPolicyKey(wsdlBinding); key = NamingHelper.GetUniqueName(keyBase, policyKeyIsUnique, null); policyKeys.Add(key, key); AttachItemPolicy(assertions, key, policyWsdl, wsdlBinding); } foreach (OperationDescription operation in endpoint.Contract.Operations) { if (!WsdlExporter.OperationIsExportable(operation)) { continue; } assertions = policyContext.GetOperationBindingAssertions(operation); // Add [wsdl:Binding/wsdl:operation] policy if (assertions.Count > 0) { WsdlNS.OperationBinding wsdlOperationBinding = endpointContext.GetOperationBinding(operation); keyBase = CreateOperationBindingPolicyKey(wsdlOperationBinding); key = NamingHelper.GetUniqueName(keyBase, policyKeyIsUnique, null); policyKeys.Add(key, key); AttachItemPolicy(assertions, key, policyWsdl, wsdlOperationBinding); } // // Add [wsdl:Binding/wsdl:operation] child policy // foreach (MessageDescription message in operation.Messages) { assertions = policyContext.GetMessageBindingAssertions(message); // Add [wsdl:Binding/wsdl:operation/wsdl:(input, output, message)] policy if (assertions.Count > 0) { WsdlNS.MessageBinding wsdlMessageBinding = endpointContext.GetMessageBinding(message); keyBase = CreateMessageBindingPolicyKey(wsdlMessageBinding, message.Direction); key = NamingHelper.GetUniqueName(keyBase, policyKeyIsUnique, null); policyKeys.Add(key, key); AttachItemPolicy(assertions, key, policyWsdl, wsdlMessageBinding); } } foreach (FaultDescription fault in operation.Faults) { assertions = policyContext.GetFaultBindingAssertions(fault); // Add [wsdl:Binding/wsdl:operation/wsdl:fault] policy if (assertions.Count > 0) { WsdlNS.FaultBinding wsdlFaultBinding = endpointContext.GetFaultBinding(fault); keyBase = CreateFaultBindingPolicyKey(wsdlFaultBinding); key = NamingHelper.GetUniqueName(keyBase, policyKeyIsUnique, null); policyKeys.Add(key, key); AttachItemPolicy(assertions, key, policyWsdl, wsdlFaultBinding); } } } } void AttachItemPolicy(ICollection assertions, string key, WsdlNS.ServiceDescription policyWsdl, WsdlNS.DocumentableItem item) { string policyKey = InsertPolicy(key, policyWsdl, assertions); InsertPolicyReference(policyKey, item); } void InsertPolicyReference(string policyKey, WsdlNS.DocumentableItem item) { //Create wsp:PolicyReference Element On DocumentableItem //--------------------------------------------------------------------------------------------------------- XmlElement policyReferenceElement = XmlDoc.CreateElement(MetadataStrings.WSPolicy.Prefix, MetadataStrings.WSPolicy.Elements.PolicyReference, policyVersion.Namespace); //Create wsp:PolicyURIs Attribute On DocumentableItem //--------------------------------------------------------------------------------------------------------- XmlAttribute uriAttribute = XmlDoc.CreateAttribute(MetadataStrings.WSPolicy.Attributes.URI); uriAttribute.Value = policyKey; policyReferenceElement.Attributes.Append(uriAttribute); item.Extensions.Add(policyReferenceElement); } string InsertPolicy(string key, WsdlNS.ServiceDescription policyWsdl, ICollection assertions) { // Create [wsp:Policy] XmlElement policyElement = CreatePolicyElement(assertions); //Create [wsp:Policy/@wsu:Id] XmlAttribute idAttribute = XmlDoc.CreateAttribute(MetadataStrings.Wsu.Prefix, MetadataStrings.Wsu.Attributes.Id, MetadataStrings.Wsu.NamespaceUri); idAttribute.Value = key; policyElement.SetAttributeNode(idAttribute); // Add wsp:Policy To WSDL if (policyWsdl != null) { policyWsdl.Extensions.Add(policyElement); } return string.Format(CultureInfo.InvariantCulture, "#{0}", key); } XmlElement CreatePolicyElement(ICollection assertions) { // Create [wsp:Policy] XmlElement policyElement = XmlDoc.CreateElement(MetadataStrings.WSPolicy.Prefix, MetadataStrings.WSPolicy.Elements.Policy, policyVersion.Namespace); // Create [wsp:Policy/wsp:ExactlyOne] XmlElement exactlyOneElement = XmlDoc.CreateElement(MetadataStrings.WSPolicy.Prefix, MetadataStrings.WSPolicy.Elements.ExactlyOne, policyVersion.Namespace); policyElement.AppendChild(exactlyOneElement); // Create [wsp:Policy/wsp:ExactlyOne/wsp:All] XmlElement allElement = XmlDoc.CreateElement(MetadataStrings.WSPolicy.Prefix, MetadataStrings.WSPolicy.Elements.All, policyVersion.Namespace); exactlyOneElement.AppendChild(allElement); // Add [wsp:Policy/wsp:ExactlyOne/wsp:All/*] foreach (XmlElement assertion in assertions) { XmlNode iNode = XmlDoc.ImportNode(assertion, true); allElement.AppendChild(iNode); } return policyElement; } static string CreateBindingPolicyKey(WsdlNS.Binding wsdlBinding) { return string.Format(CultureInfo.InvariantCulture, "{0}_policy", wsdlBinding.Name); } static string CreateOperationBindingPolicyKey(WsdlNS.OperationBinding wsdlOperationBinding) { return string.Format(CultureInfo.InvariantCulture, "{0}_{1}_policy", wsdlOperationBinding.Binding.Name, wsdlOperationBinding.Name); } static string CreateMessageBindingPolicyKey(WsdlNS.MessageBinding wsdlMessageBinding, MessageDirection direction) { WsdlNS.OperationBinding wsdlOperationBinding = wsdlMessageBinding.OperationBinding; WsdlNS.Binding wsdlBinding = wsdlOperationBinding.Binding; if (direction == MessageDirection.Input) return string.Format(CultureInfo.InvariantCulture, "{0}_{1}_Input_policy", wsdlBinding.Name, wsdlOperationBinding.Name); else return string.Format(CultureInfo.InvariantCulture, "{0}_{1}_output_policy", wsdlBinding.Name, wsdlOperationBinding.Name); } static string CreateFaultBindingPolicyKey(WsdlNS.FaultBinding wsdlFaultBinding) { WsdlNS.OperationBinding wsdlOperationBinding = wsdlFaultBinding.OperationBinding; WsdlNS.Binding wsdlBinding = wsdlOperationBinding.Binding; if (string.IsNullOrEmpty(wsdlFaultBinding.Name)) { return string.Format(CultureInfo.InvariantCulture, "{0}_{1}_Fault", wsdlBinding.Name, wsdlOperationBinding.Name); } else { return string.Format(CultureInfo.InvariantCulture, "{0}_{1}_{2}_Fault", wsdlBinding.Name, wsdlOperationBinding.Name, wsdlFaultBinding.Name); } } } class WsdlNamespaceHelper { XmlSerializerNamespaces xmlSerializerNamespaces; PolicyVersion policyVersion; internal XmlSerializerNamespaces SerializerNamespaces { get { if (xmlSerializerNamespaces == null) { XmlSerializerNamespaceWrapper namespaces = new XmlSerializerNamespaceWrapper(); namespaces.Add("wsdl", WsdlNS.ServiceDescription.Namespace); namespaces.Add("xsd", XmlSchema.Namespace); namespaces.Add(MetadataStrings.WSPolicy.Prefix, policyVersion.Namespace); namespaces.Add(MetadataStrings.Wsu.Prefix, MetadataStrings.Wsu.NamespaceUri); namespaces.Add(MetadataStrings.Addressing200408.Prefix, MetadataStrings.Addressing200408.NamespaceUri); namespaces.Add(MetadataStrings.Addressing200408.Policy.Prefix, MetadataStrings.Addressing200408.Policy.NamespaceUri); namespaces.Add(MetadataStrings.Addressing10.Prefix, MetadataStrings.Addressing10.NamespaceUri); namespaces.Add(MetadataStrings.Addressing10.WsdlBindingPolicy.Prefix, MetadataStrings.Addressing10.WsdlBindingPolicy.NamespaceUri); namespaces.Add(MetadataStrings.Addressing10.MetadataPolicy.Prefix, MetadataStrings.Addressing10.MetadataPolicy.NamespaceUri); namespaces.Add(MetadataStrings.MetadataExchangeStrings.Prefix, MetadataStrings.MetadataExchangeStrings.Namespace); namespaces.Add(NetSessionHelper.Prefix, NetSessionHelper.NamespaceUri); namespaces.Add("soapenc", "http://schemas.xmlsoap.org/soap/encoding/"); namespaces.Add("soap12", "http://schemas.xmlsoap.org/wsdl/soap12/"); namespaces.Add("soap", "http://schemas.xmlsoap.org/wsdl/soap/"); xmlSerializerNamespaces = namespaces.GetNamespaces(); } return xmlSerializerNamespaces; } } internal WsdlNamespaceHelper(PolicyVersion policyVersion) { this.policyVersion = policyVersion; } // doesn't care if you add a duplicate prefix class XmlSerializerNamespaceWrapper { readonly XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces(); readonly Dictionary lookup = new Dictionary(); internal void Add(string prefix, string namespaceUri) { if (!lookup.ContainsKey(prefix)) { namespaces.Add(prefix, namespaceUri); lookup.Add(prefix, namespaceUri); } } internal XmlSerializerNamespaces GetNamespaces() { return namespaces; } } internal static string FindOrCreatePrefix(string prefixBase, string ns, params WsdlNS.DocumentableItem[] scopes) { if (!(scopes.Length > 0)) { Fx.Assert("You must pass at least one namespaceScope"); throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "You must pass at least one namespaceScope"))); } string prefix = null; if (string.IsNullOrEmpty(ns)) { prefix = string.Empty; } else { //See if a prefix for the namespace has already been defined at one of the scopes for (int j = 0; j < scopes.Length; j++) if (TryMatchNamespace(scopes[j].Namespaces.ToArray(), ns, out prefix)) return prefix; // Create prefix definition at the nearest scope. int i = 0; prefix = prefixBase + i.ToString(CultureInfo.InvariantCulture); //[....], consider do we need to check at higher scopes as well? while (PrefixExists(scopes[0].Namespaces.ToArray(), prefix)) prefix = prefixBase + (++i).ToString(CultureInfo.InvariantCulture); } scopes[0].Namespaces.Add(prefix, ns); return prefix; } static bool PrefixExists(XmlQualifiedName[] prefixDefinitions, string prefix) { return Array.Exists(prefixDefinitions, delegate(XmlQualifiedName prefixDef) { if (prefixDef.Name == prefix) { return true; } return false; }); } static bool TryMatchNamespace(XmlQualifiedName[] prefixDefinitions, string ns, out string prefix) { string foundPrefix = null; Array.Find(prefixDefinitions, delegate(XmlQualifiedName prefixDef) { if (prefixDef.Namespace == ns) { foundPrefix = prefixDef.Name; return true; } return false; }); prefix = foundPrefix; return foundPrefix != null; } } internal static class WsdlNamingHelper { internal static XmlQualifiedName GetPortTypeQName(ContractDescription contract) { return new XmlQualifiedName(contract.Name, contract.Namespace); } internal static XmlQualifiedName GetBindingQName(ServiceEndpoint endpoint, WsdlExporter exporter, out bool wasUniquified) { // due to problems in Sysytem.Web.Services.Descriprion.ServiceDescription.Write() (double encoding) method we cannot use encoded names for // wsdl:binding item: we need to make sure that XmlConvert.EncodeLocalName will not find any problems with the name, and leave it unchanged. // consider changing the name here to something that will not be encoded by XmlSerializer (GenerateSimpleXmlName()?) string localName = endpoint.Name; string bindingWsdlNamespace = endpoint.Binding.Namespace; string uniquifiedLocalName = NamingHelper.GetUniqueName(localName, WsdlBindingQNameExists(exporter, bindingWsdlNamespace), null); wasUniquified = localName != uniquifiedLocalName; return new XmlQualifiedName(uniquifiedLocalName, bindingWsdlNamespace); } static NamingHelper.DoesNameExist WsdlBindingQNameExists(WsdlExporter exporter, string bindingWsdlNamespace) { return delegate(string localName, object nameCollection) { XmlQualifiedName wsdlBindingQName = new XmlQualifiedName(localName, bindingWsdlNamespace); WsdlNS.ServiceDescription wsdl = exporter.wsdlDocuments[bindingWsdlNamespace]; if (wsdl != null && wsdl.Bindings[localName] != null) return true; return false; }; } internal static string GetPortName(ServiceEndpoint endpoint, WsdlNS.Service wsdlService) { return NamingHelper.GetUniqueName(endpoint.Name, ServiceContainsPort(wsdlService), null); } static NamingHelper.DoesNameExist ServiceContainsPort(WsdlNS.Service service) { return delegate(string portName, object nameCollection) { foreach (WsdlNS.Port port in service.Ports) if (port.Name == portName) return true; return false; }; } internal static string GetWsdlOperationName(OperationDescription operationDescription, ContractDescription parentContractDescription) { return operationDescription.Name; } } internal static class NetSessionHelper { internal const string NamespaceUri = "http://schemas.microsoft.com/ws/2005/12/wsdl/contract"; internal const string Prefix = "msc"; internal const string UsingSession = "usingSession"; internal const string IsInitiating = "isInitiating"; internal const string IsTerminating = "isTerminating"; internal const string True = "true"; internal const string False = "false"; internal static void AddUsingSessionAttributeIfNeeded(WsdlNS.PortType wsdlPortType, ContractDescription contract) { bool sessionValue; if (contract.SessionMode == SessionMode.Required) { sessionValue = true; } else if (contract.SessionMode == SessionMode.NotAllowed) { sessionValue = false; } else { return; } wsdlPortType.ExtensibleAttributes = CloneAndAddToAttributes(wsdlPortType.ExtensibleAttributes, NetSessionHelper.Prefix, NetSessionHelper.UsingSession, NetSessionHelper.NamespaceUri, ToValue(sessionValue)); } internal static void AddInitiatingTerminatingAttributesIfNeeded(WsdlNS.Operation wsdlOperation, OperationDescription operation, ContractDescription contract) { if (contract.SessionMode == SessionMode.Required) { AddInitiatingAttribute(wsdlOperation, operation.IsInitiating); AddTerminatingAttribute(wsdlOperation, operation.IsTerminating); } } static void AddInitiatingAttribute(System.Web.Services.Description.Operation wsdlOperation, bool isInitiating) { wsdlOperation.ExtensibleAttributes = CloneAndAddToAttributes(wsdlOperation.ExtensibleAttributes, NetSessionHelper.Prefix, NetSessionHelper.IsInitiating, NetSessionHelper.NamespaceUri, ToValue(isInitiating)); } static void AddTerminatingAttribute(System.Web.Services.Description.Operation wsdlOperation, bool isTerminating) { wsdlOperation.ExtensibleAttributes = CloneAndAddToAttributes(wsdlOperation.ExtensibleAttributes, NetSessionHelper.Prefix, NetSessionHelper.IsTerminating, NetSessionHelper.NamespaceUri, ToValue(isTerminating)); } static XmlAttribute[] CloneAndAddToAttributes(XmlAttribute[] originalAttributes, string prefix, string localName, string ns, string value) { XmlAttribute newAttribute = XmlDoc.CreateAttribute(prefix, localName, ns); newAttribute.Value = value; int originalAttributeCount = 0; if (originalAttributes != null) originalAttributeCount = originalAttributes.Length; XmlAttribute[] attributes = new XmlAttribute[originalAttributeCount + 1]; if (originalAttributes != null) originalAttributes.CopyTo(attributes, 0); attributes[attributes.Length - 1] = newAttribute; return attributes; } static string ToValue(bool b) { return b ? NetSessionHelper.True : NetSessionHelper.False; } } void CallExtension(WsdlContractConversionContext contractContext, IWsdlExportExtension extension) { try { extension.ExportContract(this, contractContext); } #pragma warning suppress 56500 // covered by FxCOP catch (Exception e) { if (Fx.IsFatal(e)) throw; throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(ThrowExtensionException(contractContext.Contract, extension, e)); } } void CallExtension(WsdlEndpointConversionContext endpointContext, IWsdlExportExtension extension) { try { extension.ExportEndpoint(this, endpointContext); } #pragma warning suppress 56500 // covered by FxCOP catch (Exception e) { if (Fx.IsFatal(e)) throw; throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(ThrowExtensionException(endpointContext.Endpoint, extension, e)); } } Exception ThrowExtensionException(ContractDescription contract, IWsdlExportExtension exporter, Exception e) { string contractIdentifier = new XmlQualifiedName(contract.Name, contract.Namespace).ToString(); string errorMessage = SR.GetString(SR.WsdlExtensionContractExportError, exporter.GetType(), contractIdentifier); return new InvalidOperationException(errorMessage, e); } Exception ThrowExtensionException(ServiceEndpoint endpoint, IWsdlExportExtension exporter, Exception e) { string endpointIdentifier; if (endpoint.Address != null && endpoint.Address.Uri != null) endpointIdentifier = endpoint.Address.Uri.ToString(); else endpointIdentifier = String.Format(CultureInfo.InvariantCulture, "Contract={1}:{0} ,Binding={3}:{2}", endpoint.Contract.Name, endpoint.Contract.Namespace, endpoint.Binding.Name, endpoint.Binding.Namespace); string errorMessage = SR.GetString(SR.WsdlExtensionEndpointExportError, exporter.GetType(), endpointIdentifier); return new InvalidOperationException(errorMessage, e); } sealed class BindingDictionaryKey { public readonly ContractDescription Contract; public readonly Binding Binding; public BindingDictionaryKey(ContractDescription contract, Binding binding) { this.Contract = contract; this.Binding = binding; } public override bool Equals(object obj) { BindingDictionaryKey key = obj as BindingDictionaryKey; if (key != null && key.Binding == this.Binding && key.Contract == this.Contract) return true; return false; } public override int GetHashCode() { return this.Contract.GetHashCode() ^ this.Binding.GetHashCode(); } } sealed class EndpointDictionaryKey { public readonly ServiceEndpoint Endpoint; public readonly XmlQualifiedName ServiceQName; public EndpointDictionaryKey(ServiceEndpoint endpoint, XmlQualifiedName serviceQName) { this.Endpoint = endpoint; this.ServiceQName = serviceQName; } public override bool Equals(object obj) { EndpointDictionaryKey key = obj as EndpointDictionaryKey; if (key != null && key.Endpoint == this.Endpoint && key.ServiceQName == this.ServiceQName) return true; return false; } public override int GetHashCode() { return this.Endpoint.GetHashCode() ^ this.ServiceQName.GetHashCode(); } } } }