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 binding)
158 return ImportBinding (binding, 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 ("PolicyReference"))
242 var uri = xml.GetAttribute ("URI");
244 if (!uri.StartsWith ("#")) {
247 "Failed to resolve unknown policy reference `{0}' for " +
248 "binding `{1}'.", uri, binding.Name);
252 foreach (var sext in binding.ServiceDescription.Extensions) {
253 var sxml = sext as XmlElement;
256 if (!sxml.NamespaceURI.Equals (Constants.WspNamespace))
258 if (!sxml.LocalName.Equals ("Policy"))
260 var id = sxml.GetAttribute ("Id", Constants.WsuNamespace);
261 if (!uri.Substring (1).Equals (id))
263 context.AddPolicyAssertion (sxml);
267 foreach (IPolicyImportExtension extension in PolicyImportExtensions) {
269 extension.ImportPolicy (this, context);
270 } catch (Exception ex) {
272 "PolicyImportException `{0}' threw an exception while " +
273 "trying to import policy references for endpoint `{1}': {2}",
274 extension.GetType ().Name, endpoint.Name, ex.Message);
279 PortType GetPortTypeFromBinding (WSBinding binding)
281 foreach (WSServiceDescription sd in wsdl_documents) {
282 var port_type = sd.PortTypes [binding.Type.Name];
283 if (port_type != null)
287 throw new MetadataImportException (AddError (
288 "PortType named {0} not found in namespace {1}.",
289 binding.Type.Name, binding.Type.Namespace));
292 public override Collection<ContractDescription> ImportAllContracts ()
294 if (contracts != null)
297 contracts = new Collection<ContractDescription> ();
299 foreach (WSServiceDescription sd in wsdl_documents) {
300 foreach (PortType pt in sd.PortTypes) {
301 var cd = ImportContract (pt, false);
310 public override ServiceEndpointCollection ImportAllEndpoints ()
312 if (endpoint_colln != null)
313 return endpoint_colln;
315 endpoint_colln = new ServiceEndpointCollection ();
317 foreach (WSServiceDescription wsd in wsdl_documents) {
318 foreach (Service service in wsd.Services) {
319 foreach (Port port in service.Ports) {
320 var sep = ImportEndpoint (port, false);
322 endpoint_colln.Add (sep);
327 return endpoint_colln;
330 public ContractDescription ImportContract (PortType wsdlPortType)
332 return ImportContract (wsdlPortType, true);
335 ContractDescription ImportContract (PortType portType, bool throwOnError)
337 if (contractHash.ContainsKey (portType)) {
338 var cd = contractHash [portType];
345 throw new InvalidOperationException (String.Format (
346 "Failed to import contract for port type `{0}', " +
347 "an error has already been reported.", portType.Name));
351 var cd = DoImportContract (portType);
352 contractHash.Add (portType, cd);
354 } catch (MetadataImportException) {
355 contractHash.Add (portType, null);
359 } catch (Exception ex) {
360 contractHash.Add (portType, null);
361 var error = AddError (
362 "Failed to import contract for port type `{0}': {1}",
363 portType.Name, ex.Message);
365 throw new MetadataImportException (error, ex);
370 ContractDescription DoImportContract (PortType wsdlPortType)
374 ContractDescription cd = new ContractDescription (wsdlPortType.Name, wsdlPortType.ServiceDescription.TargetNamespace);
376 foreach (Operation op in wsdlPortType.Operations) {
377 OperationDescription op_descr = new OperationDescription (op.Name, cd);
379 foreach (OperationMessage opmsg in op.Messages) {
380 /* OperationMessageCollection */
381 MessageDescription msg_descr;
382 MessageDirection dir = MessageDirection.Input;
385 if (opmsg.GetType () == typeof (OperationInput))
386 dir = MessageDirection.Input;
387 else if (opmsg.GetType () == typeof (OperationOutput))
388 dir = MessageDirection.Output;
389 /* FIXME: OperationFault--> OperationDescription.Faults ? */
391 if (opmsg.ExtensibleAttributes != null) {
392 for (int i = 0; i < opmsg.ExtensibleAttributes.Length; i++) {
393 if (opmsg.ExtensibleAttributes [i].LocalName == "Action" &&
394 opmsg.ExtensibleAttributes [i].NamespaceURI == "http://www.w3.org/2006/05/addressing/wsdl")
395 /* addressing:Action */
396 action = opmsg.ExtensibleAttributes [i].Value;
397 /* FIXME: other attributes ? */
401 // fill Action from operation binding if required.
403 if (dir != MessageDirection.Input)
404 action = GetActionFromOperationBinding (wsdlPortType, op.Name);
409 msg_descr = new MessageDescription (action, dir);
410 /* FIXME: Headers ? */
412 op_descr.Messages.Add (msg_descr);
415 cd.Operations.Add (op_descr);
418 WsdlContractConversionContext context = new WsdlContractConversionContext (cd, wsdlPortType);
419 foreach (IWsdlImportExtension extension in wsdl_extensions)
420 extension.ImportContract (this, context);
425 string GetActionFromOperationBinding (PortType pt, string opName)
427 foreach (WSBinding binding in pt.ServiceDescription.Bindings) {
428 foreach (OperationBinding ob in binding.Operations) {
429 if (ob.Name != opName)
431 foreach (var ext in ob.Extensions) {
432 var sob = ext as SoapOperationBinding;
435 return sob.SoapAction;
443 public ServiceEndpoint ImportEndpoint (Port wsdlPort)
445 return ImportEndpoint (wsdlPort, true);
448 ServiceEndpoint ImportEndpoint (Port port, bool throwOnError)
450 ServiceEndpoint endpoint;
451 if (endpointHash.ContainsKey (port)) {
452 endpoint = endpointHash [port];
453 if (endpoint != null)
459 throw new InvalidOperationException (String.Format (
460 "Failed to import port `{0}', an error has " +
461 "already been reported before.", port.Name));
464 var binding = port.Service.ServiceDescription.Bindings [port.Binding.Name];
465 if (binding == null) {
466 endpointHash.Add (port, null);
467 var error = AddError (
468 "Failed to import port `{0}': cannot find binding `{1}' that " +
469 "this port depends on.", port.Name, port.Binding.Name);
471 throw new MetadataImportException (error);
476 endpoint = ImportBinding (binding, throwOnError);
477 } catch (Exception ex) {
478 endpointHash.Add (port, null);
479 var error = AddError (
480 "Failed to import port `{0}': error while trying to import " +
481 "binding `{1}' that this port depends on: {2}",
482 port.Name, port.Binding.Name, ex.Message);
484 throw new MetadataImportException (error, ex);
488 if (endpoint == null) {
489 endpointHash.Add (port, null);
491 "Failed to import port `{0}': error while trying to import " +
492 "binding `{1}' that this port depends on.",
493 port.Name, port.Binding.Name);
498 ImportEndpoint (port, binding, endpoint, throwOnError);
499 endpointHash.Add (port, endpoint);
501 } catch (MetadataImportException) {
502 endpointHash.Add (port, null);
506 } catch (Exception ex) {
507 endpointHash.Add (port, null);
508 var error = AddError (
509 "Failed to import port `{0}': {1}", port.Name, ex.Message);
511 throw new MetadataImportException (error, ex);
516 void ImportEndpoint (Port port, WSBinding wsb, ServiceEndpoint sep, bool throwOnError)
520 var port_type = GetPortTypeFromBinding (wsb);
522 var contract_context = new WsdlContractConversionContext (sep.Contract, port_type);
523 WsdlEndpointConversionContext endpoint_context = new WsdlEndpointConversionContext (
524 contract_context, sep, port, wsb);
526 foreach (IWsdlImportExtension extension in wsdl_extensions)
527 extension.ImportEndpoint (this, endpoint_context);
530 void ImportEndpoints (ServiceEndpointCollection coll, WSBinding binding)
532 foreach (WSServiceDescription wsd in wsdl_documents) {
533 foreach (WS.Service service in wsd.Services) {
534 foreach (WS.Port port in service.Ports) {
535 if (!binding.Name.Equals (port.Binding.Name))
537 var sep = ImportEndpoint (port, false);
545 public ServiceEndpointCollection ImportEndpoints (WSBinding binding)
547 var coll = new ServiceEndpointCollection ();
548 ImportEndpoints (coll, binding);
552 public ServiceEndpointCollection ImportEndpoints (PortType portType)
554 var coll = new ServiceEndpointCollection ();
556 foreach (WSServiceDescription wsd in wsdl_documents) {
557 foreach (WS.Binding binding in wsd.Bindings) {
558 if (!binding.Type.Name.Equals (portType.Name))
561 ImportEndpoints (coll, binding);
568 public ServiceEndpointCollection ImportEndpoints (Service service)
570 var coll = new ServiceEndpointCollection ();
572 foreach (Port port in service.Ports) {
573 var sep = ImportEndpoint (port, false);