5 // Atsushi Enomoto <atsushi@ximian.com>
6 // Ankit Jain <jankit@novell.com>
7 // Martin Baulig <martin.baulig@xamarin.com>
9 // Copyright (C) 2005 Novell, Inc. http://www.novell.com
10 // Copyright (c) 2012 Xamarin Inc. (http://www.xamarin.com)
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 using System.Collections.Generic;
33 using System.Collections.ObjectModel;
34 using System.ServiceModel;
35 using System.ServiceModel.Channels;
36 using System.Web.Services.Description;
38 using System.Xml.Schema;
40 using SMBinding = System.ServiceModel.Channels.Binding;
41 using WS = System.Web.Services.Description;
42 using WSServiceDescription = System.Web.Services.Description.ServiceDescription;
43 using WSBinding = System.Web.Services.Description.Binding;
44 using WSMessage = System.Web.Services.Description.Message;
45 using QName = System.Xml.XmlQualifiedName;
47 namespace System.ServiceModel.Description
50 public class WsdlImporter : MetadataImporter
52 ServiceDescriptionCollection wsdl_documents;
53 XmlSchemaSet xmlschemas;
54 List<XmlElement> policies; /* ?? */
56 bool beforeImportCalled;
58 KeyedByTypeCollection<IWsdlImportExtension> wsdl_extensions;
61 Collection<ContractDescription> contracts = null;
62 ServiceEndpointCollection endpoint_colln = null;
64 // Contract by PortType
65 Dictionary<PortType, ContractDescription> contractHash = null;
66 // ServiceEndpoint by WSBinding
67 Dictionary<WSBinding, ServiceEndpoint> bindingHash = null;
68 // ServiceEndpoint by Port
69 Dictionary<Port, ServiceEndpoint> endpointHash = null;
73 IEnumerable<IPolicyImportExtension> policyImportExtensions,
74 IEnumerable<IWsdlImportExtension> wsdlImportExtensions)
75 : base (policyImportExtensions)
78 throw new ArgumentNullException ("metadata");
80 if (wsdlImportExtensions == null) {
81 wsdl_extensions = new KeyedByTypeCollection<IWsdlImportExtension> ();
83 wsdl_extensions.Add (new DataContractSerializerMessageContractImporter ());
84 wsdl_extensions.Add (new XmlSerializerMessageContractImporter ());
85 wsdl_extensions.Add (new MessageEncodingBindingElementImporter ());
86 wsdl_extensions.Add (new TransportBindingElementImporter ());
87 wsdl_extensions.Add (new StandardBindingImporter ());
89 wsdl_extensions = new KeyedByTypeCollection<IWsdlImportExtension> (wsdlImportExtensions);
92 // It is okay to fill these members immediately when WsdlImporter.ctor() is invoked
93 // i.e. after this .ctor(), those metadata docs are not considered anymore.
94 this.metadata = metadata;
95 this.wsdl_documents = new ServiceDescriptionCollection ();
96 this.xmlschemas = new XmlSchemaSet ();
97 this.policies = new List<XmlElement> ();
98 this.contractHash = new Dictionary<PortType, ContractDescription> ();
99 this.bindingHash = new Dictionary<WSBinding, ServiceEndpoint> ();
100 this.endpointHash = new Dictionary<Port, ServiceEndpoint> ();
102 foreach (MetadataSection ms in metadata.MetadataSections) {
103 if (ms.Dialect == MetadataSection.ServiceDescriptionDialect &&
104 ms.Metadata.GetType () == typeof (WSServiceDescription))
105 wsdl_documents.Add ((WSServiceDescription) ms.Metadata);
107 if (ms.Dialect == MetadataSection.XmlSchemaDialect &&
108 ms.Metadata.GetType () == typeof (XmlSchema))
109 xmlschemas.Add ((XmlSchema) ms.Metadata);
113 public WsdlImporter (MetadataSet metadata)
114 : this (metadata, null, null)
118 public ServiceDescriptionCollection WsdlDocuments {
119 get { return wsdl_documents; }
122 public KeyedByTypeCollection <IWsdlImportExtension> WsdlImportExtensions {
123 get { return wsdl_extensions; }
126 public XmlSchemaSet XmlSchemas {
127 get { return xmlschemas; }
130 public Collection<SMBinding> ImportAllBindings ()
132 Collection<SMBinding> bindings = new Collection<SMBinding> ();
134 foreach (WSServiceDescription sd in wsdl_documents) {
135 foreach (WSBinding binding in sd.Bindings) {
136 var endpoint = ImportBinding (binding, false);
137 if (endpoint != null)
138 bindings.Add (endpoint.Binding);
147 if (beforeImportCalled)
150 foreach (IWsdlImportExtension extension in wsdl_extensions)
151 extension.BeforeImport (wsdl_documents, xmlschemas, policies);
153 beforeImportCalled = true;
156 public SMBinding ImportBinding (WSBinding wsdlBinding)
158 return ImportBinding (wsdlBinding, true).Binding;
161 ServiceEndpoint ImportBinding (WSBinding binding, bool throwOnError)
163 if (bindingHash.ContainsKey (binding)) {
164 var sep = bindingHash [binding];
171 throw new InvalidOperationException (String.Format (
172 "Failed to import binding {0}, an error has " +
173 "already been reported before.", binding.Name));
177 var port_type = GetPortTypeFromBinding (binding);
178 var contract = ImportContract (port_type);
179 var contract_context = new WsdlContractConversionContext (contract, port_type);
181 var sep = ImportBinding (binding, contract_context);
182 bindingHash.Add (binding, sep);
184 } catch (MetadataImportException) {
185 bindingHash.Add (binding, null);
189 } catch (Exception ex) {
190 bindingHash.Add (binding, null);
191 var error = AddError (
192 "Failed to import binding `{0}': {1}", binding.Name, ex.Message);
194 throw new MetadataImportException (error, ex);
199 ServiceEndpoint ImportBinding (WSBinding binding,
200 WsdlContractConversionContext contract_context)
204 var sep = new ServiceEndpoint (contract_context.Contract);
206 var custom = new CustomBinding ();
207 custom.Name = binding.Name;
208 custom.Namespace = binding.ServiceDescription.TargetNamespace;
210 sep.Binding = custom;
213 ImportPolicy (binding, sep);
214 } catch (Exception ex) {
215 // FIXME: Policy import is still experimental.
216 AddWarning ("Exception while trying to import policy for " +
217 "binding `{0}': {1}", binding.Name, ex.Message);
220 var endpoint_context = new WsdlEndpointConversionContext (
221 contract_context, sep, null, binding);
223 foreach (IWsdlImportExtension extension in wsdl_extensions)
224 extension.ImportEndpoint (this, endpoint_context);
229 void ImportPolicy (WSBinding binding, ServiceEndpoint endpoint)
231 var context = new Description.CustomPolicyConversionContext (binding, endpoint);
232 var assertions = context.GetBindingAssertions ();
234 foreach (var ext in binding.Extensions) {
235 var xml = ext as XmlElement;
238 if (!xml.NamespaceURI.Equals (Constants.WspNamespace))
240 if (xml.LocalName.Equals ("Policy")) {
241 context.AddPolicyAssertion (xml);
244 if (!xml.LocalName.Equals ("PolicyReference"))
246 var uri = xml.GetAttribute ("URI");
248 if (!uri.StartsWith ("#")) {
251 "Failed to resolve unknown policy reference `{0}' for " +
252 "binding `{1}'.", uri, binding.Name);
256 foreach (var sext in binding.ServiceDescription.Extensions) {
257 var sxml = sext as XmlElement;
260 if (!sxml.NamespaceURI.Equals (Constants.WspNamespace))
262 if (!sxml.LocalName.Equals ("Policy"))
264 var id = sxml.GetAttribute ("Id", Constants.WsuNamespace);
265 if (!uri.Substring (1).Equals (id))
267 context.AddPolicyAssertion (sxml);
271 foreach (IPolicyImportExtension extension in PolicyImportExtensions) {
273 extension.ImportPolicy (this, context);
274 } catch (Exception ex) {
276 "PolicyImportException `{0}' threw an exception while " +
277 "trying to import policy references for endpoint `{1}': {2}",
278 extension.GetType ().Name, endpoint.Name, ex.Message);
283 PortType GetPortTypeFromBinding (WSBinding binding)
285 foreach (WSServiceDescription sd in wsdl_documents) {
286 var port_type = sd.PortTypes [binding.Type.Name];
287 if (port_type != null)
291 throw new MetadataImportException (AddError (
292 "PortType named {0} not found in namespace {1}.",
293 binding.Type.Name, binding.Type.Namespace));
296 public override Collection<ContractDescription> ImportAllContracts ()
298 if (contracts != null)
301 contracts = new Collection<ContractDescription> ();
303 foreach (WSServiceDescription sd in wsdl_documents) {
304 foreach (PortType pt in sd.PortTypes) {
305 var cd = ImportContract (pt, false);
314 public override ServiceEndpointCollection ImportAllEndpoints ()
316 if (endpoint_colln != null)
317 return endpoint_colln;
319 endpoint_colln = new ServiceEndpointCollection ();
321 foreach (WSServiceDescription wsd in wsdl_documents) {
322 foreach (Service service in wsd.Services) {
323 foreach (Port port in service.Ports) {
324 var sep = ImportEndpoint (port, false);
326 endpoint_colln.Add (sep);
331 return endpoint_colln;
334 public ContractDescription ImportContract (PortType wsdlPortType)
336 return ImportContract (wsdlPortType, true);
339 ContractDescription ImportContract (PortType portType, bool throwOnError)
341 if (contractHash.ContainsKey (portType)) {
342 var cd = contractHash [portType];
349 throw new InvalidOperationException (String.Format (
350 "Failed to import contract for port type `{0}', " +
351 "an error has already been reported.", portType.Name));
355 var cd = DoImportContract (portType);
356 contractHash.Add (portType, cd);
358 } catch (MetadataImportException) {
359 contractHash.Add (portType, null);
363 } catch (Exception ex) {
364 contractHash.Add (portType, null);
365 var error = AddError (
366 "Failed to import contract for port type `{0}': {1}",
367 portType.Name, ex.Message);
369 throw new MetadataImportException (error, ex);
374 ContractDescription DoImportContract (PortType wsdlPortType)
378 ContractDescription cd = new ContractDescription (wsdlPortType.Name, wsdlPortType.ServiceDescription.TargetNamespace);
380 foreach (Operation op in wsdlPortType.Operations) {
381 OperationDescription op_descr = new OperationDescription (op.Name, cd);
383 foreach (OperationMessage opmsg in op.Messages) {
384 /* OperationMessageCollection */
385 MessageDescription msg_descr;
386 MessageDirection dir = MessageDirection.Input;
389 if (opmsg.GetType () == typeof (OperationInput))
390 dir = MessageDirection.Input;
391 else if (opmsg.GetType () == typeof (OperationOutput))
392 dir = MessageDirection.Output;
393 /* FIXME: OperationFault--> OperationDescription.Faults ? */
395 if (opmsg.ExtensibleAttributes != null) {
396 for (int i = 0; i < opmsg.ExtensibleAttributes.Length; i++) {
397 if (opmsg.ExtensibleAttributes [i].LocalName == "Action" &&
398 opmsg.ExtensibleAttributes [i].NamespaceURI == "http://www.w3.org/2006/05/addressing/wsdl")
399 /* addressing:Action */
400 action = opmsg.ExtensibleAttributes [i].Value;
401 /* FIXME: other attributes ? */
405 // fill Action from operation binding if required.
407 if (dir != MessageDirection.Input)
408 action = GetActionFromOperationBinding (wsdlPortType, op.Name);
413 msg_descr = new MessageDescription (action, dir);
414 /* FIXME: Headers ? */
416 op_descr.Messages.Add (msg_descr);
419 cd.Operations.Add (op_descr);
422 WsdlContractConversionContext context = new WsdlContractConversionContext (cd, wsdlPortType);
423 foreach (IWsdlImportExtension extension in wsdl_extensions)
424 extension.ImportContract (this, context);
429 string GetActionFromOperationBinding (PortType pt, string opName)
431 foreach (WSBinding binding in pt.ServiceDescription.Bindings) {
432 foreach (OperationBinding ob in binding.Operations) {
433 if (ob.Name != opName)
435 foreach (var ext in ob.Extensions) {
436 var sob = ext as SoapOperationBinding;
439 return sob.SoapAction;
447 public ServiceEndpoint ImportEndpoint (Port wsdlPort)
449 return ImportEndpoint (wsdlPort, true);
452 ServiceEndpoint ImportEndpoint (Port port, bool throwOnError)
454 ServiceEndpoint endpoint;
455 if (endpointHash.ContainsKey (port)) {
456 endpoint = endpointHash [port];
457 if (endpoint != null)
463 throw new InvalidOperationException (String.Format (
464 "Failed to import port `{0}', an error has " +
465 "already been reported before.", port.Name));
468 var binding = port.Service.ServiceDescription.Bindings [port.Binding.Name];
469 if (binding == null) {
470 endpointHash.Add (port, null);
471 var error = AddError (
472 "Failed to import port `{0}': cannot find binding `{1}' that " +
473 "this port depends on.", port.Name, port.Binding.Name);
475 throw new MetadataImportException (error);
480 endpoint = ImportBinding (binding, throwOnError);
481 } catch (Exception ex) {
482 endpointHash.Add (port, null);
483 var error = AddError (
484 "Failed to import port `{0}': error while trying to import " +
485 "binding `{1}' that this port depends on: {2}",
486 port.Name, port.Binding.Name, ex.Message);
488 throw new MetadataImportException (error, ex);
492 if (endpoint == null) {
493 endpointHash.Add (port, null);
495 "Failed to import port `{0}': error while trying to import " +
496 "binding `{1}' that this port depends on.",
497 port.Name, port.Binding.Name);
502 ImportEndpoint (port, binding, endpoint, throwOnError);
503 endpointHash.Add (port, endpoint);
505 } catch (MetadataImportException) {
506 endpointHash.Add (port, null);
510 } catch (Exception ex) {
511 endpointHash.Add (port, null);
512 var error = AddError (
513 "Failed to import port `{0}': {1}", port.Name, ex.Message);
515 throw new MetadataImportException (error, ex);
520 void ImportEndpoint (Port port, WSBinding wsb, ServiceEndpoint sep, bool throwOnError)
524 var port_type = GetPortTypeFromBinding (wsb);
526 var contract_context = new WsdlContractConversionContext (sep.Contract, port_type);
527 WsdlEndpointConversionContext endpoint_context = new WsdlEndpointConversionContext (
528 contract_context, sep, port, wsb);
530 foreach (IWsdlImportExtension extension in wsdl_extensions)
531 extension.ImportEndpoint (this, endpoint_context);
534 void ImportEndpoints (ServiceEndpointCollection coll, WSBinding binding)
536 foreach (WSServiceDescription wsd in wsdl_documents) {
537 foreach (WS.Service service in wsd.Services) {
538 foreach (WS.Port port in service.Ports) {
539 if (!binding.Name.Equals (port.Binding.Name))
541 var sep = ImportEndpoint (port, false);
549 public ServiceEndpointCollection ImportEndpoints (WSBinding wsdlBinding)
551 var coll = new ServiceEndpointCollection ();
552 ImportEndpoints (coll, wsdlBinding);
556 public ServiceEndpointCollection ImportEndpoints (PortType wsdlPortType)
558 var coll = new ServiceEndpointCollection ();
560 foreach (WSServiceDescription wsd in wsdl_documents) {
561 foreach (WS.Binding binding in wsd.Bindings) {
562 if (!binding.Type.Name.Equals (wsdlPortType.Name))
565 ImportEndpoints (coll, binding);
572 public ServiceEndpointCollection ImportEndpoints (Service wsdlService)
574 var coll = new ServiceEndpointCollection ();
576 foreach (Port port in wsdlService.Ports) {
577 var sep = ImportEndpoint (port, false);