2 // Copyright (c) Microsoft Corporation. All rights reserved.
5 namespace System.ServiceModel.Description
7 using System.Collections.Generic;
8 using System.Diagnostics.CodeAnalysis;
9 using System.Globalization;
15 using System.Xml.Schema;
16 using System.Xml.Serialization;
17 using WsdlNS = System.Web.Services.Description;
19 internal static class WsdlHelper
21 public static WsdlNS.ServiceDescription GetSingleWsdl(MetadataSet metadataSet)
23 if (metadataSet.MetadataSections.Count < 1)
28 List<WsdlNS.ServiceDescription> wsdls = new List<WsdlNS.ServiceDescription>();
29 List<XmlSchema> xsds = new List<XmlSchema>();
31 foreach (MetadataSection section in metadataSet.MetadataSections)
33 if (section.Metadata is WsdlNS.ServiceDescription)
35 wsdls.Add((WsdlNS.ServiceDescription)section.Metadata);
38 if (section.Metadata is XmlSchema)
40 xsds.Add((XmlSchema)section.Metadata);
44 VerifyContractNamespace(wsdls);
45 WsdlNS.ServiceDescription singleWsdl = GetSingleWsdl(CopyServiceDescriptionCollection(wsdls));
48 foreach (XmlSchema schema in xsds)
50 XmlSchema newSchema = CloneXsd(schema);
51 RemoveSchemaLocations(newSchema);
52 singleWsdl.Types.Schemas.Add(newSchema);
58 private static void RemoveSchemaLocations(XmlSchema schema)
60 foreach (XmlSchemaObject schemaObject in schema.Includes)
62 XmlSchemaExternal external = schemaObject as XmlSchemaExternal;
65 external.SchemaLocation = null;
70 private static WsdlNS.ServiceDescription GetSingleWsdl(List<WsdlNS.ServiceDescription> wsdls)
72 // Use WSDL that has the contracts as the base for single WSDL
73 WsdlNS.ServiceDescription singleWsdl = wsdls.First(wsdl => wsdl.PortTypes.Count > 0);
74 if (singleWsdl == null)
76 singleWsdl = new WsdlNS.ServiceDescription();
80 singleWsdl.Types.Schemas.Clear();
81 singleWsdl.Imports.Clear();
84 Dictionary<XmlQualifiedName, XmlQualifiedName> bindingReferenceChanges = new Dictionary<XmlQualifiedName, XmlQualifiedName>();
85 foreach (WsdlNS.ServiceDescription wsdl in wsdls)
87 if (wsdl != singleWsdl)
89 MergeWsdl(singleWsdl, wsdl, bindingReferenceChanges);
93 EnsureSingleNamespace(singleWsdl, bindingReferenceChanges);
97 private static List<WsdlNS.ServiceDescription> CopyServiceDescriptionCollection(List<WsdlNS.ServiceDescription> wsdls)
99 List<WsdlNS.ServiceDescription> newWsdls = new List<WsdlNS.ServiceDescription>();
100 foreach (WsdlNS.ServiceDescription wsdl in wsdls)
102 newWsdls.Add(CloneWsdl(wsdl));
108 private static void MergeWsdl(WsdlNS.ServiceDescription singleWsdl, WsdlNS.ServiceDescription wsdl, Dictionary<XmlQualifiedName, XmlQualifiedName> bindingReferenceChanges)
110 if (wsdl.Services.Count > 0)
112 singleWsdl.Name = wsdl.Name;
115 foreach (WsdlNS.Binding binding in wsdl.Bindings)
117 string uniqueBindingName = NamingHelper.GetUniqueName(binding.Name, WsdlHelper.IsBindingNameUsed, singleWsdl.Bindings);
118 if (binding.Name != uniqueBindingName)
120 bindingReferenceChanges.Add(
121 new XmlQualifiedName(binding.Name, binding.ServiceDescription.TargetNamespace),
122 new XmlQualifiedName(uniqueBindingName, singleWsdl.TargetNamespace));
123 UpdatePolicyKeys(binding, uniqueBindingName, wsdl);
124 binding.Name = uniqueBindingName;
127 singleWsdl.Bindings.Add(binding);
130 foreach (object extension in wsdl.Extensions)
132 singleWsdl.Extensions.Add(extension);
135 foreach (WsdlNS.Message message in wsdl.Messages)
137 singleWsdl.Messages.Add(message);
140 foreach (WsdlNS.Service service in wsdl.Services)
142 singleWsdl.Services.Add(service);
145 foreach (string warning in wsdl.ValidationWarnings)
147 singleWsdl.ValidationWarnings.Add(warning);
151 private static void UpdatePolicyKeys(WsdlNS.Binding binding, string newBindingName, WsdlNS.ServiceDescription wsdl)
153 string oldBindingName = binding.Name;
156 IEnumerable<XmlElement> bindingPolicies = FindAllElements(wsdl.Extensions, MetadataStrings.WSPolicy.Elements.Policy);
157 string policyIdStringPrefixFormat = "{0}_";
158 foreach (XmlElement policyElement in bindingPolicies)
160 XmlNode policyId = policyElement.Attributes.GetNamedItem(MetadataStrings.Wsu.Attributes.Id, MetadataStrings.Wsu.NamespaceUri);
161 string policyIdString = policyId.Value;
162 string policyIdStringWithOldBindingName = string.Format(CultureInfo.InvariantCulture, policyIdStringPrefixFormat, oldBindingName);
163 string policyIdStringWithNewBindingName = string.Format(CultureInfo.InvariantCulture, policyIdStringPrefixFormat, newBindingName);
164 if (policyId != null && policyIdString != null && policyIdString.StartsWith(policyIdStringWithOldBindingName, StringComparison.Ordinal))
166 policyId.Value = policyIdStringWithNewBindingName + policyIdString.Substring(policyIdStringWithOldBindingName.Length);
171 UpdatePolicyReference(binding.Extensions, oldBindingName, newBindingName);
172 foreach (WsdlNS.OperationBinding operationBinding in binding.Operations)
174 UpdatePolicyReference(operationBinding.Extensions, oldBindingName, newBindingName);
175 if (operationBinding.Input != null)
177 UpdatePolicyReference(operationBinding.Input.Extensions, oldBindingName, newBindingName);
180 if (operationBinding.Output != null)
182 UpdatePolicyReference(operationBinding.Output.Extensions, oldBindingName, newBindingName);
185 foreach (WsdlNS.FaultBinding fault in operationBinding.Faults)
187 UpdatePolicyReference(fault.Extensions, oldBindingName, newBindingName);
192 private static void UpdatePolicyReference(WsdlNS.ServiceDescriptionFormatExtensionCollection extensions, string oldBindingName, string newBindingName)
194 IEnumerable<XmlElement> bindingPolicyReferences = FindAllElements(extensions, MetadataStrings.WSPolicy.Elements.PolicyReference);
195 string policyReferencePrefixFormat = "#{0}_";
196 foreach (XmlElement policyReferenceElement in bindingPolicyReferences)
198 XmlNode policyReference = policyReferenceElement.Attributes.GetNamedItem(MetadataStrings.WSPolicy.Attributes.URI);
199 string policyReferenceValue = policyReference.Value;
200 string policyReferenceValueWithOldBindingName = string.Format(CultureInfo.InvariantCulture, policyReferencePrefixFormat, oldBindingName);
201 string policyReferenceValueWithNewBindingName = string.Format(CultureInfo.InvariantCulture, policyReferencePrefixFormat, newBindingName);
202 if (policyReference != null && policyReferenceValue != null && policyReferenceValue.StartsWith(policyReferenceValueWithOldBindingName, StringComparison.Ordinal))
204 policyReference.Value = policyReferenceValueWithNewBindingName + policyReference.Value.Substring(policyReferenceValueWithOldBindingName.Length);
209 private static IEnumerable<XmlElement> FindAllElements(WsdlNS.ServiceDescriptionFormatExtensionCollection extensions, string elementName)
211 List<XmlElement> policyReferences = new List<XmlElement>();
212 for (int i = 0; i < extensions.Count; i++)
214 XmlElement element = extensions[i] as XmlElement;
215 if (element != null && element.LocalName == elementName)
217 policyReferences.Add(element);
221 return policyReferences;
224 private static void VerifyContractNamespace(List<WsdlNS.ServiceDescription> wsdls)
226 IEnumerable<WsdlNS.ServiceDescription> contractWsdls = wsdls.Where(serviceDescription => serviceDescription.PortTypes.Count > 0);
227 if (contractWsdls.Count() > 1)
229 IEnumerable<string> namespaces = contractWsdls.Select<WsdlNS.ServiceDescription, string>(wsdl => wsdl.TargetNamespace);
230 string contractNamespaces = string.Join(", ", namespaces);
231 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SingleWsdlNotGenerated, contractNamespaces)));
235 private static void EnsureSingleNamespace(WsdlNS.ServiceDescription wsdl, Dictionary<XmlQualifiedName, XmlQualifiedName> bindingReferenceChanges)
237 string targetNamespace = wsdl.TargetNamespace;
238 foreach (WsdlNS.Binding binding in wsdl.Bindings)
240 if (binding.Type.Namespace != targetNamespace)
242 binding.Type = new XmlQualifiedName(binding.Type.Name, targetNamespace);
246 foreach (WsdlNS.PortType portType in wsdl.PortTypes)
248 foreach (WsdlNS.Operation operation in portType.Operations)
250 WsdlNS.OperationInput messageInput = operation.Messages.Input;
251 if (messageInput != null && messageInput.Message.Namespace != targetNamespace)
253 messageInput.Message = new XmlQualifiedName(messageInput.Message.Name, targetNamespace);
256 WsdlNS.OperationOutput messageOutput = operation.Messages.Output;
257 if (messageOutput != null && messageOutput.Message.Namespace != targetNamespace)
259 messageOutput.Message = new XmlQualifiedName(messageOutput.Message.Name, targetNamespace);
262 foreach (WsdlNS.OperationFault fault in operation.Faults)
264 if (fault.Message.Namespace != targetNamespace)
266 fault.Message = new XmlQualifiedName(fault.Message.Name, targetNamespace);
272 foreach (WsdlNS.Service service in wsdl.Services)
274 foreach (WsdlNS.Port port in service.Ports)
276 XmlQualifiedName newPortBinding;
277 if (bindingReferenceChanges.TryGetValue(port.Binding, out newPortBinding))
279 port.Binding = newPortBinding;
281 else if (port.Binding.Namespace != targetNamespace)
283 port.Binding = new XmlQualifiedName(port.Binding.Name, targetNamespace);
289 private static bool IsBindingNameUsed(string name, object collection)
291 WsdlNS.BindingCollection bindings = (WsdlNS.BindingCollection)collection;
292 foreach (WsdlNS.Binding binding in bindings)
294 if (binding.Name == name)
303 private static WsdlNS.ServiceDescription CloneWsdl(WsdlNS.ServiceDescription originalWsdl)
305 Fx.Assert(originalWsdl != null, "originalWsdl must not be null");
306 WsdlNS.ServiceDescription newWsdl;
307 using (MemoryStream memoryStream = new MemoryStream())
309 originalWsdl.Write(memoryStream);
310 memoryStream.Seek(0, SeekOrigin.Begin);
311 newWsdl = WsdlNS.ServiceDescription.Read(memoryStream);
317 [SuppressMessage("Microsoft.Security.Xml", "CA3054:DoNotAllowDtdOnXmlTextReader")]
318 [SuppressMessage("Microsoft.Security.Xml", "CA3069:ReviewDtdProcessingAssignment", Justification = "This is trusted server code from the application only. We should allow the customer add dtd.")]
319 private static XmlSchema CloneXsd(XmlSchema originalXsd)
321 Fx.Assert(originalXsd != null, "originalXsd must not be null");
323 using (MemoryStream memoryStream = new MemoryStream())
325 originalXsd.Write(memoryStream);
326 memoryStream.Seek(0, SeekOrigin.Begin);
327 newXsd = XmlSchema.Read(new XmlTextReader(memoryStream) { DtdProcessing = DtdProcessing.Parse }, null);