// // WsdlImporter.cs // // Authors: // Atsushi Enomoto // Ankit Jain // Martin Baulig // // Copyright (C) 2005 Novell, Inc. http://www.novell.com // Copyright (c) 2012 Xamarin Inc. (http://www.xamarin.com) // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ServiceModel; using System.ServiceModel.Channels; using System.Web.Services.Description; using System.Xml; using System.Xml.Schema; using SMBinding = System.ServiceModel.Channels.Binding; using WS = System.Web.Services.Description; using WSServiceDescription = System.Web.Services.Description.ServiceDescription; using WSBinding = System.Web.Services.Description.Binding; using WSMessage = System.Web.Services.Description.Message; using QName = System.Xml.XmlQualifiedName; namespace System.ServiceModel.Description { [MonoTODO] public class WsdlImporter : MetadataImporter { ServiceDescriptionCollection wsdl_documents; XmlSchemaSet xmlschemas; List policies; /* ?? */ MetadataSet metadata; bool beforeImportCalled; KeyedByTypeCollection wsdl_extensions; //Imported Collection contracts = null; ServiceEndpointCollection endpoint_colln = null; // Contract by PortType Dictionary contractHash = null; // ServiceEndpoint by WSBinding Dictionary bindingHash = null; // ServiceEndpoint by Port Dictionary endpointHash = null; public WsdlImporter ( MetadataSet metadata, IEnumerable policyImportExtensions, IEnumerable wsdlImportExtensions) : base (policyImportExtensions) { if (metadata == null) throw new ArgumentNullException ("metadata"); if (wsdlImportExtensions == null) { wsdl_extensions = new KeyedByTypeCollection (); wsdl_extensions.Add (new DataContractSerializerMessageContractImporter ()); wsdl_extensions.Add (new XmlSerializerMessageContractImporter ()); wsdl_extensions.Add (new MessageEncodingBindingElementImporter ()); wsdl_extensions.Add (new TransportBindingElementImporter ()); wsdl_extensions.Add (new StandardBindingImporter ()); } else { wsdl_extensions = new KeyedByTypeCollection (wsdlImportExtensions); } // It is okay to fill these members immediately when WsdlImporter.ctor() is invoked // i.e. after this .ctor(), those metadata docs are not considered anymore. this.metadata = metadata; this.wsdl_documents = new ServiceDescriptionCollection (); this.xmlschemas = new XmlSchemaSet (); this.policies = new List (); this.contractHash = new Dictionary (); this.bindingHash = new Dictionary (); this.endpointHash = new Dictionary (); foreach (MetadataSection ms in metadata.MetadataSections) { if (ms.Dialect == MetadataSection.ServiceDescriptionDialect && ms.Metadata.GetType () == typeof (WSServiceDescription)) wsdl_documents.Add ((WSServiceDescription) ms.Metadata); else if (ms.Dialect == MetadataSection.XmlSchemaDialect && ms.Metadata.GetType () == typeof (XmlSchema)) xmlschemas.Add ((XmlSchema) ms.Metadata); } } public WsdlImporter (MetadataSet metadata) : this (metadata, null, null) { } public ServiceDescriptionCollection WsdlDocuments { get { return wsdl_documents; } } public KeyedByTypeCollection WsdlImportExtensions { get { return wsdl_extensions; } } public XmlSchemaSet XmlSchemas { get { return xmlschemas; } } public Collection ImportAllBindings () { Collection bindings = new Collection (); foreach (WSServiceDescription sd in wsdl_documents) { foreach (WSBinding binding in sd.Bindings) { var endpoint = ImportBinding (binding, false); if (endpoint != null) bindings.Add (endpoint.Binding); } } return bindings; } void BeforeImport () { if (beforeImportCalled) return; foreach (IWsdlImportExtension extension in wsdl_extensions) extension.BeforeImport (wsdl_documents, xmlschemas, policies); beforeImportCalled = true; } public SMBinding ImportBinding (WSBinding wsdlBinding) { return ImportBinding (wsdlBinding, true).Binding; } ServiceEndpoint ImportBinding (WSBinding binding, bool throwOnError) { if (bindingHash.ContainsKey (binding)) { var sep = bindingHash [binding]; if (sep != null) return sep; if (!throwOnError) return null; throw new InvalidOperationException (String.Format ( "Failed to import binding {0}, an error has " + "already been reported before.", binding.Name)); } try { var port_type = GetPortTypeFromBinding (binding); var contract = ImportContract (port_type); var contract_context = new WsdlContractConversionContext (contract, port_type); var sep = ImportBinding (binding, contract_context); bindingHash.Add (binding, sep); return sep; } catch (MetadataImportException) { bindingHash.Add (binding, null); if (throwOnError) throw; return null; } catch (Exception ex) { bindingHash.Add (binding, null); var error = AddError ( "Failed to import binding `{0}': {1}", binding.Name, ex.Message); if (throwOnError) throw new MetadataImportException (error, ex); return null; } } ServiceEndpoint ImportBinding (WSBinding binding, WsdlContractConversionContext contract_context) { BeforeImport (); var sep = new ServiceEndpoint (contract_context.Contract); var custom = new CustomBinding (); custom.Name = binding.Name; custom.Namespace = binding.ServiceDescription.TargetNamespace; sep.Binding = custom; try { ImportPolicy (binding, sep); } catch (Exception ex) { // FIXME: Policy import is still experimental. AddWarning ("Exception while trying to import policy for " + "binding `{0}': {1}", binding.Name, ex.Message); } var endpoint_context = new WsdlEndpointConversionContext ( contract_context, sep, null, binding); foreach (IWsdlImportExtension extension in wsdl_extensions) extension.ImportEndpoint (this, endpoint_context); return sep; } void ImportPolicy (WSBinding binding, ServiceEndpoint endpoint) { var context = new Description.CustomPolicyConversionContext (binding, endpoint); var assertions = context.GetBindingAssertions (); foreach (var ext in binding.Extensions) { var xml = ext as XmlElement; if (xml == null) continue; if (!xml.NamespaceURI.Equals (Constants.WspNamespace)) continue; if (xml.LocalName.Equals ("Policy")) { context.AddPolicyAssertion (xml); continue; } if (!xml.LocalName.Equals ("PolicyReference")) continue; var uri = xml.GetAttribute ("URI"); if (!uri.StartsWith ("#")) { // FIXME AddWarning ( "Failed to resolve unknown policy reference `{0}' for " + "binding `{1}'.", uri, binding.Name); continue; } foreach (var sext in binding.ServiceDescription.Extensions) { var sxml = sext as XmlElement; if (sxml == null) continue; if (!sxml.NamespaceURI.Equals (Constants.WspNamespace)) continue; if (!sxml.LocalName.Equals ("Policy")) continue; var id = sxml.GetAttribute ("Id", Constants.WsuNamespace); if (!uri.Substring (1).Equals (id)) continue; context.AddPolicyAssertion (sxml); } } foreach (IPolicyImportExtension extension in PolicyImportExtensions) { try { extension.ImportPolicy (this, context); } catch (Exception ex) { AddWarning ( "PolicyImportException `{0}' threw an exception while " + "trying to import policy references for endpoint `{1}': {2}", extension.GetType ().Name, endpoint.Name, ex.Message); } } } PortType GetPortTypeFromBinding (WSBinding binding) { foreach (WSServiceDescription sd in wsdl_documents) { var port_type = sd.PortTypes [binding.Type.Name]; if (port_type != null) return port_type; } throw new MetadataImportException (AddError ( "PortType named {0} not found in namespace {1}.", binding.Type.Name, binding.Type.Namespace)); } public override Collection ImportAllContracts () { if (contracts != null) return contracts; contracts = new Collection (); foreach (WSServiceDescription sd in wsdl_documents) { foreach (PortType pt in sd.PortTypes) { var cd = ImportContract (pt, false); if (cd != null) contracts.Add (cd); } } return contracts; } public override ServiceEndpointCollection ImportAllEndpoints () { if (endpoint_colln != null) return endpoint_colln; endpoint_colln = new ServiceEndpointCollection (); foreach (WSServiceDescription wsd in wsdl_documents) { foreach (Service service in wsd.Services) { foreach (Port port in service.Ports) { var sep = ImportEndpoint (port, false); if (sep != null) endpoint_colln.Add (sep); } } } return endpoint_colln; } public ContractDescription ImportContract (PortType wsdlPortType) { return ImportContract (wsdlPortType, true); } ContractDescription ImportContract (PortType portType, bool throwOnError) { if (contractHash.ContainsKey (portType)) { var cd = contractHash [portType]; if (cd != null) return cd; if (!throwOnError) return null; throw new InvalidOperationException (String.Format ( "Failed to import contract for port type `{0}', " + "an error has already been reported.", portType.Name)); } try { var cd = DoImportContract (portType); contractHash.Add (portType, cd); return cd; } catch (MetadataImportException) { contractHash.Add (portType, null); if (throwOnError) throw; return null; } catch (Exception ex) { contractHash.Add (portType, null); var error = AddError ( "Failed to import contract for port type `{0}': {1}", portType.Name, ex.Message); if (throwOnError) throw new MetadataImportException (error, ex); return null; } } ContractDescription DoImportContract (PortType wsdlPortType) { BeforeImport (); ContractDescription cd = new ContractDescription (wsdlPortType.Name, wsdlPortType.ServiceDescription.TargetNamespace); foreach (Operation op in wsdlPortType.Operations) { OperationDescription op_descr = new OperationDescription (op.Name, cd); foreach (OperationMessage opmsg in op.Messages) { /* OperationMessageCollection */ MessageDescription msg_descr; MessageDirection dir = MessageDirection.Input; string action = ""; if (opmsg.GetType () == typeof (OperationInput)) dir = MessageDirection.Input; else if (opmsg.GetType () == typeof (OperationOutput)) dir = MessageDirection.Output; /* FIXME: OperationFault--> OperationDescription.Faults ? */ if (opmsg.ExtensibleAttributes != null) { for (int i = 0; i < opmsg.ExtensibleAttributes.Length; i++) { if (opmsg.ExtensibleAttributes [i].LocalName == "Action" && opmsg.ExtensibleAttributes [i].NamespaceURI == "http://www.w3.org/2006/05/addressing/wsdl") /* addressing:Action */ action = opmsg.ExtensibleAttributes [i].Value; /* FIXME: other attributes ? */ } } // fill Action from operation binding if required. if (action == "") { if (dir != MessageDirection.Input) action = GetActionFromOperationBinding (wsdlPortType, op.Name); else action = "*"; } msg_descr = new MessageDescription (action, dir); /* FIXME: Headers ? */ op_descr.Messages.Add (msg_descr); } cd.Operations.Add (op_descr); } WsdlContractConversionContext context = new WsdlContractConversionContext (cd, wsdlPortType); foreach (IWsdlImportExtension extension in wsdl_extensions) extension.ImportContract (this, context); return cd; } string GetActionFromOperationBinding (PortType pt, string opName) { foreach (WSBinding binding in pt.ServiceDescription.Bindings) { foreach (OperationBinding ob in binding.Operations) { if (ob.Name != opName) continue; foreach (var ext in ob.Extensions) { var sob = ext as SoapOperationBinding; if (sob == null) continue; return sob.SoapAction; } return String.Empty; } } return String.Empty; } public ServiceEndpoint ImportEndpoint (Port wsdlPort) { return ImportEndpoint (wsdlPort, true); } ServiceEndpoint ImportEndpoint (Port port, bool throwOnError) { ServiceEndpoint endpoint; if (endpointHash.ContainsKey (port)) { endpoint = endpointHash [port]; if (endpoint != null) return endpoint; if (!throwOnError) return null; throw new InvalidOperationException (String.Format ( "Failed to import port `{0}', an error has " + "already been reported before.", port.Name)); } var binding = port.Service.ServiceDescription.Bindings [port.Binding.Name]; if (binding == null) { endpointHash.Add (port, null); var error = AddError ( "Failed to import port `{0}': cannot find binding `{1}' that " + "this port depends on.", port.Name, port.Binding.Name); if (throwOnError) throw new MetadataImportException (error); return null; } try { endpoint = ImportBinding (binding, throwOnError); } catch (Exception ex) { endpointHash.Add (port, null); var error = AddError ( "Failed to import port `{0}': error while trying to import " + "binding `{1}' that this port depends on: {2}", port.Name, port.Binding.Name, ex.Message); if (throwOnError) throw new MetadataImportException (error, ex); return null; } if (endpoint == null) { endpointHash.Add (port, null); AddError ( "Failed to import port `{0}': error while trying to import " + "binding `{1}' that this port depends on.", port.Name, port.Binding.Name); return null; } try { ImportEndpoint (port, binding, endpoint, throwOnError); endpointHash.Add (port, endpoint); return endpoint; } catch (MetadataImportException) { endpointHash.Add (port, null); if (throwOnError) throw; return null; } catch (Exception ex) { endpointHash.Add (port, null); var error = AddError ( "Failed to import port `{0}': {1}", port.Name, ex.Message); if (throwOnError) throw new MetadataImportException (error, ex); return null; } } void ImportEndpoint (Port port, WSBinding wsb, ServiceEndpoint sep, bool throwOnError) { BeforeImport (); var port_type = GetPortTypeFromBinding (wsb); var contract_context = new WsdlContractConversionContext (sep.Contract, port_type); WsdlEndpointConversionContext endpoint_context = new WsdlEndpointConversionContext ( contract_context, sep, port, wsb); foreach (IWsdlImportExtension extension in wsdl_extensions) extension.ImportEndpoint (this, endpoint_context); } void ImportEndpoints (ServiceEndpointCollection coll, WSBinding binding) { foreach (WSServiceDescription wsd in wsdl_documents) { foreach (WS.Service service in wsd.Services) { foreach (WS.Port port in service.Ports) { if (!binding.Name.Equals (port.Binding.Name)) continue; var sep = ImportEndpoint (port, false); if (sep != null) coll.Add (sep); } } } } public ServiceEndpointCollection ImportEndpoints (WSBinding wsdlBinding) { var coll = new ServiceEndpointCollection (); ImportEndpoints (coll, wsdlBinding); return coll; } public ServiceEndpointCollection ImportEndpoints (PortType wsdlPortType) { var coll = new ServiceEndpointCollection (); foreach (WSServiceDescription wsd in wsdl_documents) { foreach (WS.Binding binding in wsd.Bindings) { if (!binding.Type.Name.Equals (wsdlPortType.Name)) continue; ImportEndpoints (coll, binding); } } return coll; } public ServiceEndpointCollection ImportEndpoints (Service wsdlService) { var coll = new ServiceEndpointCollection (); foreach (Port port in wsdlService.Ports) { var sep = ImportEndpoint (port, false); if (sep != null) coll.Add (sep); } return coll; } } }