5 // Atsushi Enomoto <atsushi@ximian.com>
6 // Ankit Jain <JAnkit@novell.com>
8 // Copyright (C) 2005 Novell, Inc. http://www.novell.com
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 using System.Collections;
31 using System.Collections.Generic;
32 using System.ServiceModel;
33 using System.ServiceModel.Channels;
34 using System.Web.Services.Description;
36 using System.Xml.Schema;
37 using System.Xml.Serialization;
38 using System.Runtime.Serialization;
40 using SMBinding = System.ServiceModel.Channels.Binding;
41 using SMMessage = System.ServiceModel.Channels.Message;
43 using WSServiceDescription = System.Web.Services.Description.ServiceDescription;
44 using WSBinding = System.Web.Services.Description.Binding;
45 using WSMessage = System.Web.Services.Description.Message;
46 using QName = System.Xml.XmlQualifiedName;
48 namespace System.ServiceModel.Description
51 public class WsdlExporter : MetadataExporter
53 private MetadataSet metadata;
54 private ServiceDescriptionCollection wsdl_colln;
55 private XsdDataContractExporter xsd_exporter;
56 private Hashtable exported_contracts;
58 public override MetadataSet GetGeneratedMetadata ()
63 metadata = new MetadataSet ();
64 foreach (WSServiceDescription sd in GeneratedWsdlDocuments)
65 metadata.MetadataSections.Add (
66 MetadataSection.CreateFromServiceDescription (sd));
68 foreach (XmlSchema xs in GeneratedXmlSchemas.Schemas ())
69 metadata.MetadataSections.Add (
70 MetadataSection.CreateFromSchema (xs));
75 public override void ExportContract (ContractDescription contract)
77 ExportContractInternal (contract);
80 List<IWsdlExportExtension> ExportContractInternal (ContractDescription contract)
82 QName qname = new QName (contract.Name, contract.Namespace);
83 if (ExportedContracts.ContainsKey (qname))
84 throw new ArgumentException (String.Format (
85 "A ContractDescription with Namespace : {0} and Name : {1} has already been exported.",
86 contract.Namespace, contract.Name));
88 WSServiceDescription sd = GetServiceDescription (contract.Namespace);
90 List<IWsdlExportExtension> extensions = new List<IWsdlExportExtension> ();
91 foreach (IWsdlExportExtension extn in contract.Behaviors.FindAll<IWsdlExportExtension> ())
92 extensions.Add (extn);
94 XmlDocument xdoc = new XmlDocument ();
96 PortType ws_port = new PortType ();
97 ws_port.Name = contract.Name;
99 foreach (OperationDescription sm_op in contract.Operations) {
100 Operation ws_op = new Operation ();
101 ws_op.Name = sm_op.Name;
103 foreach (MessageDescription sm_md in sm_op.Messages) {
105 OperationMessage ws_opmsg;
106 WSMessage ws_msg = new WSMessage ();
107 MessagePart ws_msgpart;
108 if (sm_md.Direction == MessageDirection.Input) {
109 ws_opmsg = new OperationInput ();
110 ws_msg.Name = String.Concat (ws_port.Name, "_", ws_op.Name, "_", "InputMessage");
111 ws_msgpart = ExportMessageBodyDescription (sm_md.Body, ws_op.Name, sd.TargetNamespace);
113 ws_opmsg = new OperationOutput ();
114 ws_msg.Name = String.Concat (ws_port.Name, "_", ws_op.Name, "_", "OutputMessage");
115 ws_msgpart = ExportMessageBodyDescription (sm_md.Body, ws_op.Name + "Response", sd.TargetNamespace);
117 ws_msg.Parts.Add (ws_msgpart);
122 XmlAttribute attr = xdoc.CreateAttribute ("wsaw", "Action", "http://www.w3.org/2006/05/addressing/wsdl");
123 attr.Value = sm_md.Action;
124 ws_opmsg.ExtensibleAttributes = new XmlAttribute [] { attr };
126 //FIXME: Set .Input & .Output
128 ws_opmsg.Message = new QName (ws_msg.Name, sd.TargetNamespace);
129 ws_op.Messages.Add (ws_opmsg);
130 sd.Messages.Add (ws_msg);
133 ws_port.Operations.Add (ws_op);
135 foreach (IWsdlExportExtension extn in sm_op.Behaviors.FindAll<IWsdlExportExtension> ())
136 extensions.Add (extn);
139 //Add Imports for <types
140 XmlSchema xs_import = new XmlSchema ();
141 xs_import.TargetNamespace = String.Concat (
143 contract.Namespace.EndsWith ("/") ? "" : "/",
145 foreach (XmlSchema schema in GeneratedXmlSchemas.Schemas ()) {
146 XmlSchemaImport imp = new XmlSchemaImport ();
147 imp.Namespace = schema.TargetNamespace;
148 xs_import.Includes.Add (imp);
150 sd.Types.Schemas.Add (xs_import);
152 sd.PortTypes.Add (ws_port);
153 ExportedContracts [qname] = contract;
155 WsdlContractConversionContext context = new WsdlContractConversionContext (contract, ws_port);
156 foreach (IWsdlExportExtension extn in extensions)
157 extn.ExportContract (this, context);
162 public override void ExportEndpoint (ServiceEndpoint endpoint)
164 List<IWsdlExportExtension> extensions = ExportContractInternal (endpoint.Contract);
167 WSServiceDescription sd = GetServiceDescription ("http://tempuri.org/");
168 if (sd.TargetNamespace != endpoint.Contract.Namespace) {
169 sd.Namespaces.Add ("i0", endpoint.Contract.Namespace);
172 Import import = new Import ();
173 import.Namespace = endpoint.Contract.Namespace;
175 sd.Imports.Add (import);
178 if (endpoint.Binding == null)
179 throw new ArgumentException (String.Format (
180 "Binding for ServiceEndpoint named '{0}' is null",
183 bool msg_version_none =
184 endpoint.Binding.MessageVersion != null &&
185 endpoint.Binding.MessageVersion.Equals (MessageVersion.None);
187 WSBinding ws_binding = new WSBinding ();
190 ws_binding.Name = String.Concat (endpoint.Binding.Name, "_", endpoint.Contract.Name);
193 ws_binding.Type = new QName (endpoint.Contract.Name, endpoint.Contract.Namespace);
194 sd.Bindings.Add (ws_binding);
196 if (!msg_version_none) {
197 SoapBinding soap_binding = new SoapBinding ();
198 soap_binding.Transport = SoapBinding.HttpTransport;
199 soap_binding.Style = SoapBindingStyle.Document;
200 ws_binding.Extensions.Add (soap_binding);
204 foreach (OperationDescription sm_op in endpoint.Contract.Operations){
205 OperationBinding op_binding = new OperationBinding ();
206 op_binding.Name = sm_op.Name;
208 //FIXME: Move to IWsdlExportExtension .. ?
209 foreach (MessageDescription sm_md in sm_op.Messages) {
210 if (sm_md.Direction == MessageDirection.Input) {
212 InputBinding in_binding = new InputBinding ();
214 if (!msg_version_none) {
215 SoapBodyBinding soap_body_binding = new SoapBodyBinding ();
216 soap_body_binding.Use = SoapBindingUse.Literal;
217 in_binding.Extensions.Add (soap_body_binding);
220 //<operation > <soap:operation soapAction .. >
221 SoapOperationBinding soap_operation_binding = new SoapOperationBinding ();
222 soap_operation_binding.SoapAction = sm_md.Action;
223 soap_operation_binding.Style = SoapBindingStyle.Document;
224 op_binding.Extensions.Add (soap_operation_binding);
227 op_binding.Input = in_binding;
230 OutputBinding out_binding = new OutputBinding ();
232 if (!msg_version_none) {
233 SoapBodyBinding soap_body_binding = new SoapBodyBinding ();
234 soap_body_binding.Use = SoapBindingUse.Literal;
235 out_binding.Extensions.Add (soap_body_binding);
238 op_binding.Output = out_binding;
242 ws_binding.Operations.Add (op_binding);
246 Port ws_port = ExportService (sd, ws_binding, endpoint.Address, msg_version_none);
248 //Call IWsdlExportExtension.ExportEndpoint
249 WsdlContractConversionContext contract_context = new WsdlContractConversionContext (
250 endpoint.Contract, sd.PortTypes [endpoint.Contract.Name]);
251 WsdlEndpointConversionContext endpoint_context = new WsdlEndpointConversionContext (
252 contract_context, endpoint, ws_port, ws_binding);
254 foreach (IWsdlExportExtension extn in extensions)
255 extn.ExportEndpoint (this, endpoint_context);
260 Port ExportService (WSServiceDescription sd, WSBinding ws_binding, EndpointAddress address, bool msg_version_none)
265 Service ws_svc = GetService (sd, "service");
268 Port ws_port = new Port ();
269 ws_port.Name = ws_binding.Name;
270 ws_port.Binding = new QName (ws_binding.Name, sd.TargetNamespace);
272 if (!msg_version_none) {
273 SoapAddressBinding soap_addr = new SoapAddressBinding ();
274 soap_addr.Location = address.Uri.AbsoluteUri;
276 ws_port.Extensions.Add (soap_addr);
279 ws_svc.Ports.Add (ws_port);
284 Service GetService (WSServiceDescription sd, string name)
286 Service svc = sd.Services [name];
290 svc = new Service ();
292 sd.Services.Add (svc);
297 WSServiceDescription GetServiceDescription (string ns)
299 foreach (WSServiceDescription sd in GeneratedWsdlDocuments) {
300 if (sd.TargetNamespace == ns)
304 WSServiceDescription ret = new WSServiceDescription ();
305 ret.TargetNamespace = ns;
306 ret.Namespaces = GetNamespaces (ns);
307 GeneratedWsdlDocuments.Add (ret);
314 public ServiceDescriptionCollection GeneratedWsdlDocuments {
316 if (wsdl_colln == null)
317 wsdl_colln = new ServiceDescriptionCollection ();
322 public XmlSchemaSet GeneratedXmlSchemas {
323 get { return XsdExporter.Schemas; }
326 public void ExportEndpoints (
327 IEnumerable<ServiceEndpoint> endpoints,
328 XmlQualifiedName name)
330 throw new NotImplementedException ();
333 XsdDataContractExporter XsdExporter {
335 if (xsd_exporter == null)
336 xsd_exporter = new XsdDataContractExporter ();
342 Hashtable ExportedContracts {
344 if (exported_contracts == null)
345 exported_contracts = new Hashtable ();
346 return exported_contracts;
350 XmlSerializerNamespaces GetNamespaces (string target_namespace)
352 XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces ();
354 namespaces.Add ("soap", "http://schemas.xmlsoap.org/wsdl/soap/");
355 namespaces.Add ("wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");
356 namespaces.Add ("soapenc", "http://schemas.xmlsoap.org/soap/encoding/");
357 namespaces.Add ("tns", target_namespace);
358 namespaces.Add ("wsa", "http://schemas.xmlsoap.org/ws/2004/08/addressing");
359 namespaces.Add ("wsp", "http://schemas.xmlsoap.org/ws/2004/09/policy");
360 namespaces.Add ("wsap", "http://schemas.xmlsoap.org/ws/2004/08/addressing/policy");
361 namespaces.Add ("msc", "http://schemas.microsoft.com/ws/2005/12/wsdl/contract");
362 namespaces.Add ("wsaw", "http://www.w3.org/2006/05/addressing/wsdl");
363 namespaces.Add ("soap12", "http://schemas.xmlsoap.org/wsdl/soap12/");
364 namespaces.Add ("wsa10", "http://www.w3.org/2005/08/addressing");
365 namespaces.Add ("wsdl", "http://schemas.xmlsoap.org/wsdl/");
370 MessagePart ExportMessageBodyDescription (MessageBodyDescription msgbody, string name, string ns)
372 MessagePart msgpart = new MessagePart ();
373 string part_name = IsTypeMessage (msgbody);
375 if (part_name != null) {
376 msgpart.Name = part_name;
377 msgpart.Type = ExportTypeMessage (); //FIXME: Cache this
379 msgpart.Name = "parameters";
380 msgpart.Element = ExportParameters (msgbody, name, ns);
385 /* Sets the @name if the param or return type is SMMessage */
386 string IsTypeMessage (MessageBodyDescription msgbody)
388 MessagePartDescription part = null;
390 if (msgbody.Parts.Count == 0)
391 part = msgbody.ReturnValue;
392 else if (msgbody.Parts.Count == 1)
393 part = msgbody.Parts [0];
395 if (part != null && (part.Type.FullName == typeof (SMMessage).FullName))
401 QName ExportParameters (MessageBodyDescription msgbody, string name, string ns)
403 XmlSchema xs = GetSchema (ns);
404 //FIXME: Extract to a HasElement method ?
405 foreach (XmlSchemaObject o in xs.Items) {
406 XmlSchemaElement e = o as XmlSchemaElement;
411 throw new InvalidOperationException (String.Format (
412 "Message element named '{0}:{1}' has already been exported.",
416 //Create the element for "parameters"
417 XmlSchemaElement schema_element = new XmlSchemaElement ();
418 schema_element.Name = name;
420 XmlSchemaComplexType complex_type = new XmlSchemaComplexType ();
421 //Generate Sequence representing the message/parameters
422 //FIXME: MessageContractAttribute
424 XmlSchemaSequence sequence = new XmlSchemaSequence ();
425 XmlSchemaElement element = null;
427 if (msgbody.ReturnValue == null) {
429 foreach (MessagePartDescription part in msgbody.Parts) {
430 if (part.Type == null)
431 //FIXME: Eg. when WsdlImporter is used to import a wsdl
432 throw new NotImplementedException ();
434 element = GetSchemaElementForPart (part, xs);
435 sequence.Items.Add (element);
439 if (msgbody.ReturnValue.Type != typeof (void)) {
440 element = GetSchemaElementForPart (msgbody.ReturnValue, xs);
441 sequence.Items.Add (element);
445 complex_type.Particle = sequence;
446 schema_element.SchemaType = complex_type;
448 xs.Items.Add (schema_element);
449 GeneratedXmlSchemas.Reprocess (xs);
451 return new QName (schema_element.Name, xs.TargetNamespace);
454 //Exports <xs:type for SMMessage
455 //FIXME: complex type for this can be made static
456 QName ExportTypeMessage ()
458 XmlSchema xs = GetSchema ("http://schemas.microsoft.com/Message");
459 QName qname = new QName ("MessageBody", xs.TargetNamespace);
461 foreach (XmlSchemaObject o in xs.Items) {
462 XmlSchemaComplexType ct = o as XmlSchemaComplexType;
466 if (ct.Name == "MessageBody")
471 XmlSchemaComplexType complex_type = new XmlSchemaComplexType ();
472 complex_type.Name = "MessageBody";
473 XmlSchemaSequence sequence = new XmlSchemaSequence ();
475 XmlSchemaAny any = new XmlSchemaAny ();
477 any.MaxOccursString = "unbounded";
478 any.Namespace = "##any";
480 sequence.Items.Add (any);
481 complex_type.Particle = sequence;
483 xs.Items.Add (complex_type);
484 GeneratedXmlSchemas.Reprocess (xs);
489 XmlSchemaElement GetSchemaElementForPart (MessagePartDescription part, XmlSchema schema)
491 XmlSchemaElement element = new XmlSchemaElement ();
493 element.Name = part.Name;
494 XsdExporter.Export (part.Type);
495 element.SchemaTypeName = XsdExporter.GetSchemaTypeName (part.Type);
496 AddImport (schema, element.SchemaTypeName.Namespace);
498 //FIXME: nillable, minOccurs
499 if (XsdExporter.GetSchemaType (part.Type) is XmlSchemaComplexType ||
500 part.Type == typeof (string))
501 element.IsNillable = true;
502 element.MinOccurs = 0;
507 //FIXME: Replace with a dictionary ?
508 void AddImport (XmlSchema schema, string ns)
510 if (ns == XmlSchema.Namespace || schema.TargetNamespace == ns)
513 foreach (XmlSchemaObject o in schema.Includes) {
514 XmlSchemaImport import = o as XmlSchemaImport;
517 if (import.Namespace == ns)
521 XmlSchemaImport imp = new XmlSchemaImport ();
523 schema.Includes.Add (imp);
526 XmlSchema GetSchema (string ns)
528 ICollection colln = GeneratedXmlSchemas.Schemas (ns);
529 if (colln.Count > 0) {
531 throw new Exception ("More than 1 schema found for ns = " + ns);
533 foreach (object o in colln)
534 return (o as XmlSchema);
537 XmlSchema schema = new XmlSchema ();
538 schema.TargetNamespace = ns;
539 schema.ElementFormDefault = XmlSchemaForm.Qualified;
540 GeneratedXmlSchemas.Add (schema);