WsdlExporter.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  1. //
  2. // WsdlExporter.cs
  3. //
  4. // Author:
  5. // Atsushi Enomoto <[email protected]>
  6. // Ankit Jain <[email protected]>
  7. //
  8. // Copyright (C) 2005 Novell, Inc. http://www.novell.com
  9. //
  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:
  17. //
  18. // The above copyright notice and this permission notice shall be
  19. // included in all copies or substantial portions of the Software.
  20. //
  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.
  28. //
  29. using System;
  30. using System.Collections;
  31. using System.Collections.Generic;
  32. using System.ServiceModel;
  33. using System.ServiceModel.Channels;
  34. using System.Web.Services.Description;
  35. using System.Xml;
  36. using System.Xml.Schema;
  37. using System.Xml.Serialization;
  38. using System.Runtime.Serialization;
  39. using SMBinding = System.ServiceModel.Channels.Binding;
  40. using SMMessage = System.ServiceModel.Channels.Message;
  41. using WSServiceDescription = System.Web.Services.Description.ServiceDescription;
  42. using WSBinding = System.Web.Services.Description.Binding;
  43. using WSMessage = System.Web.Services.Description.Message;
  44. using QName = System.Xml.XmlQualifiedName;
  45. namespace System.ServiceModel.Description
  46. {
  47. [MonoTODO]
  48. public class WsdlExporter : MetadataExporter
  49. {
  50. private MetadataSet metadata;
  51. private ServiceDescriptionCollection wsdl_colln;
  52. private XsdDataContractExporter xsd_exporter;
  53. private Hashtable exported_contracts;
  54. public override MetadataSet GetGeneratedMetadata ()
  55. {
  56. if (metadata != null)
  57. return metadata;
  58. metadata = new MetadataSet ();
  59. foreach (WSServiceDescription sd in GeneratedWsdlDocuments)
  60. metadata.MetadataSections.Add (
  61. MetadataSection.CreateFromServiceDescription (sd));
  62. foreach (XmlSchema xs in GeneratedXmlSchemas.Schemas ())
  63. metadata.MetadataSections.Add (
  64. MetadataSection.CreateFromSchema (xs));
  65. return metadata;
  66. }
  67. public override void ExportContract (ContractDescription contract)
  68. {
  69. ExportContractInternal (contract);
  70. }
  71. List<IWsdlExportExtension> ExportContractInternal (ContractDescription contract)
  72. {
  73. QName qname = new QName (contract.Name, contract.Namespace);
  74. if (ExportedContracts.ContainsKey (qname))
  75. throw new ArgumentException (String.Format (
  76. "A ContractDescription with Namespace : {0} and Name : {1} has already been exported.",
  77. contract.Namespace, contract.Name));
  78. WSServiceDescription sd = GetServiceDescription (contract.Namespace);
  79. List<IWsdlExportExtension> extensions = new List<IWsdlExportExtension> ();
  80. foreach (IWsdlExportExtension extn in contract.Behaviors.FindAll<IWsdlExportExtension> ())
  81. extensions.Add (extn);
  82. XmlDocument xdoc = new XmlDocument ();
  83. PortType ws_port = new PortType ();
  84. ws_port.Name = contract.Name;
  85. foreach (OperationDescription sm_op in contract.Operations) {
  86. Operation ws_op = new Operation ();
  87. ws_op.Name = sm_op.Name;
  88. foreach (MessageDescription sm_md in sm_op.Messages) {
  89. //OperationMessage
  90. OperationMessage ws_opmsg;
  91. WSMessage ws_msg = new WSMessage ();
  92. MessagePart ws_msgpart;
  93. if (sm_md.Direction == MessageDirection.Input) {
  94. ws_opmsg = new OperationInput ();
  95. ws_msg.Name = String.Concat (ws_port.Name, "_", ws_op.Name, "_", "InputMessage");
  96. ws_msgpart = ExportMessageBodyDescription (sm_md.Body, ws_op.Name, sd.TargetNamespace);
  97. } else {
  98. ws_opmsg = new OperationOutput ();
  99. ws_msg.Name = String.Concat (ws_port.Name, "_", ws_op.Name, "_", "OutputMessage");
  100. ws_msgpart = ExportMessageBodyDescription (sm_md.Body, ws_op.Name + "Response", sd.TargetNamespace);
  101. }
  102. ws_msg.Parts.Add (ws_msgpart);
  103. /* FIXME: Faults */
  104. //Action
  105. XmlAttribute attr = xdoc.CreateAttribute ("wsaw", "Action", "http://www.w3.org/2006/05/addressing/wsdl");
  106. attr.Value = sm_md.Action;
  107. ws_opmsg.ExtensibleAttributes = new XmlAttribute [] { attr };
  108. //FIXME: Set .Input & .Output
  109. ws_opmsg.Message = new QName (ws_msg.Name, sd.TargetNamespace);
  110. ws_op.Messages.Add (ws_opmsg);
  111. sd.Messages.Add (ws_msg);
  112. }
  113. ws_port.Operations.Add (ws_op);
  114. foreach (IWsdlExportExtension extn in sm_op.Behaviors.FindAll<IWsdlExportExtension> ())
  115. extensions.Add (extn);
  116. }
  117. //Add Imports for <types
  118. XmlSchema xs_import = new XmlSchema ();
  119. xs_import.TargetNamespace = String.Concat (
  120. contract.Namespace,
  121. contract.Namespace.EndsWith ("/") ? "" : "/",
  122. "Imports");
  123. foreach (XmlSchema schema in GeneratedXmlSchemas.Schemas ()) {
  124. XmlSchemaImport imp = new XmlSchemaImport ();
  125. imp.Namespace = schema.TargetNamespace;
  126. xs_import.Includes.Add (imp);
  127. }
  128. sd.Types.Schemas.Add (xs_import);
  129. sd.PortTypes.Add (ws_port);
  130. ExportedContracts [qname] = contract;
  131. WsdlContractConversionContext context = new WsdlContractConversionContext (contract, ws_port);
  132. foreach (IWsdlExportExtension extn in extensions)
  133. extn.ExportContract (this, context);
  134. return extensions;
  135. }
  136. public override void ExportEndpoint (ServiceEndpoint endpoint)
  137. {
  138. List<IWsdlExportExtension> extensions = ExportContractInternal (endpoint.Contract);
  139. //FIXME: Namespace
  140. WSServiceDescription sd = GetServiceDescription ("http://tempuri.org/");
  141. if (sd.TargetNamespace != endpoint.Contract.Namespace) {
  142. sd.Namespaces.Add ("i0", endpoint.Contract.Namespace);
  143. //Import
  144. Import import = new Import ();
  145. import.Namespace = endpoint.Contract.Namespace;
  146. sd.Imports.Add (import);
  147. }
  148. if (endpoint.Binding == null)
  149. throw new ArgumentException (String.Format (
  150. "Binding for ServiceEndpoint named '{0}' is null",
  151. endpoint.Name));
  152. bool msg_version_none =
  153. endpoint.Binding.MessageVersion != null &&
  154. endpoint.Binding.MessageVersion.Equals (MessageVersion.None);
  155. //ExportBinding
  156. WSBinding ws_binding = new WSBinding ();
  157. //<binding name = ..
  158. ws_binding.Name = String.Concat (endpoint.Binding.Name, "_", endpoint.Contract.Name);
  159. //<binding type = ..
  160. ws_binding.Type = new QName (endpoint.Contract.Name, endpoint.Contract.Namespace);
  161. sd.Bindings.Add (ws_binding);
  162. if (!msg_version_none) {
  163. SoapBinding soap_binding = new SoapBinding ();
  164. soap_binding.Transport = SoapBinding.HttpTransport;
  165. soap_binding.Style = SoapBindingStyle.Document;
  166. ws_binding.Extensions.Add (soap_binding);
  167. }
  168. // <operation
  169. foreach (OperationDescription sm_op in endpoint.Contract.Operations){
  170. OperationBinding op_binding = new OperationBinding ();
  171. op_binding.Name = sm_op.Name;
  172. //FIXME: Move to IWsdlExportExtension .. ?
  173. foreach (MessageDescription sm_md in sm_op.Messages) {
  174. if (sm_md.Direction == MessageDirection.Input) {
  175. //<input
  176. InputBinding in_binding = new InputBinding ();
  177. if (!msg_version_none) {
  178. SoapBodyBinding soap_body_binding = new SoapBodyBinding ();
  179. soap_body_binding.Use = SoapBindingUse.Literal;
  180. in_binding.Extensions.Add (soap_body_binding);
  181. //Set Action
  182. //<operation > <soap:operation soapAction .. >
  183. SoapOperationBinding soap_operation_binding = new SoapOperationBinding ();
  184. soap_operation_binding.SoapAction = sm_md.Action;
  185. soap_operation_binding.Style = SoapBindingStyle.Document;
  186. op_binding.Extensions.Add (soap_operation_binding);
  187. }
  188. op_binding.Input = in_binding;
  189. } else {
  190. //<output
  191. OutputBinding out_binding = new OutputBinding ();
  192. if (!msg_version_none) {
  193. SoapBodyBinding soap_body_binding = new SoapBodyBinding ();
  194. soap_body_binding.Use = SoapBindingUse.Literal;
  195. out_binding.Extensions.Add (soap_body_binding);
  196. }
  197. op_binding.Output = out_binding;
  198. }
  199. }
  200. ws_binding.Operations.Add (op_binding);
  201. }
  202. //Add <service
  203. Port ws_port = ExportService (sd, ws_binding, endpoint.Address, msg_version_none);
  204. //Call IWsdlExportExtension.ExportEndpoint
  205. WsdlContractConversionContext contract_context = new WsdlContractConversionContext (
  206. endpoint.Contract, sd.PortTypes [endpoint.Contract.Name]);
  207. WsdlEndpointConversionContext endpoint_context = new WsdlEndpointConversionContext (
  208. contract_context, endpoint, ws_port, ws_binding);
  209. foreach (IWsdlExportExtension extn in extensions)
  210. extn.ExportEndpoint (this, endpoint_context);
  211. }
  212. Port ExportService (WSServiceDescription sd, WSBinding ws_binding, EndpointAddress address, bool msg_version_none)
  213. {
  214. if (address == null)
  215. return null;
  216. Service ws_svc = GetService (sd, "service");
  217. sd.Name = "service";
  218. Port ws_port = new Port ();
  219. ws_port.Name = ws_binding.Name;
  220. ws_port.Binding = new QName (ws_binding.Name, sd.TargetNamespace);
  221. if (!msg_version_none) {
  222. SoapAddressBinding soap_addr = new SoapAddressBinding ();
  223. soap_addr.Location = address.Uri.AbsoluteUri;
  224. ws_port.Extensions.Add (soap_addr);
  225. }
  226. ws_svc.Ports.Add (ws_port);
  227. return ws_port;
  228. }
  229. Service GetService (WSServiceDescription sd, string name)
  230. {
  231. Service svc = sd.Services [name];
  232. if (svc != null)
  233. return svc;
  234. svc = new Service ();
  235. svc.Name = name;
  236. sd.Services.Add (svc);
  237. return svc;
  238. }
  239. WSServiceDescription GetServiceDescription (string ns)
  240. {
  241. foreach (WSServiceDescription sd in GeneratedWsdlDocuments) {
  242. if (sd.TargetNamespace == ns)
  243. return sd;
  244. }
  245. WSServiceDescription ret = new WSServiceDescription ();
  246. ret.TargetNamespace = ns;
  247. ret.Namespaces = GetNamespaces (ns);
  248. GeneratedWsdlDocuments.Add (ret);
  249. metadata = null;
  250. return ret;
  251. }
  252. public ServiceDescriptionCollection GeneratedWsdlDocuments {
  253. get {
  254. if (wsdl_colln == null)
  255. wsdl_colln = new ServiceDescriptionCollection ();
  256. return wsdl_colln;
  257. }
  258. }
  259. public XmlSchemaSet GeneratedXmlSchemas {
  260. get { return XsdExporter.Schemas; }
  261. }
  262. public void ExportEndpoints (
  263. IEnumerable<ServiceEndpoint> endpoints,
  264. XmlQualifiedName name)
  265. {
  266. throw new NotImplementedException ();
  267. }
  268. XsdDataContractExporter XsdExporter {
  269. get {
  270. if (xsd_exporter == null)
  271. xsd_exporter = new XsdDataContractExporter ();
  272. return xsd_exporter;
  273. }
  274. }
  275. Hashtable ExportedContracts {
  276. get {
  277. if (exported_contracts == null)
  278. exported_contracts = new Hashtable ();
  279. return exported_contracts;
  280. }
  281. }
  282. XmlSerializerNamespaces GetNamespaces (string target_namespace)
  283. {
  284. XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces ();
  285. namespaces.Add ("soap", "http://schemas.xmlsoap.org/wsdl/soap/");
  286. namespaces.Add ("wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");
  287. namespaces.Add ("soapenc", "http://schemas.xmlsoap.org/soap/encoding/");
  288. namespaces.Add ("tns", target_namespace);
  289. namespaces.Add ("wsa", "http://schemas.xmlsoap.org/ws/2004/08/addressing");
  290. namespaces.Add ("wsp", "http://schemas.xmlsoap.org/ws/2004/09/policy");
  291. namespaces.Add ("wsap", "http://schemas.xmlsoap.org/ws/2004/08/addressing/policy");
  292. namespaces.Add ("msc", "http://schemas.microsoft.com/ws/2005/12/wsdl/contract");
  293. namespaces.Add ("wsaw", "http://www.w3.org/2006/05/addressing/wsdl");
  294. namespaces.Add ("soap12", "http://schemas.xmlsoap.org/wsdl/soap12/");
  295. namespaces.Add ("wsa10", "http://www.w3.org/2005/08/addressing");
  296. namespaces.Add ("wsdl", "http://schemas.xmlsoap.org/wsdl/");
  297. return namespaces;
  298. }
  299. MessagePart ExportMessageBodyDescription (MessageBodyDescription msgbody, string name, string ns)
  300. {
  301. MessagePart msgpart = new MessagePart ();
  302. string part_name = IsTypeMessage (msgbody);
  303. if (part_name != null) {
  304. msgpart.Name = part_name;
  305. msgpart.Type = ExportTypeMessage (); //FIXME: Cache this
  306. } else {
  307. msgpart.Name = "parameters";
  308. msgpart.Element = ExportParameters (msgbody, name, ns);
  309. }
  310. return msgpart;
  311. }
  312. /* Sets the @name if the param or return type is SMMessage */
  313. string IsTypeMessage (MessageBodyDescription msgbody)
  314. {
  315. MessagePartDescription part = null;
  316. if (msgbody.Parts.Count == 0)
  317. part = msgbody.ReturnValue;
  318. else if (msgbody.Parts.Count == 1)
  319. part = msgbody.Parts [0];
  320. if (part != null && (part.Type.FullName == typeof (SMMessage).FullName))
  321. return part.Name;
  322. return null;
  323. }
  324. QName ExportParameters (MessageBodyDescription msgbody, string name, string ns)
  325. {
  326. XmlSchema xs = GetSchema (ns);
  327. //FIXME: Extract to a HasElement method ?
  328. foreach (XmlSchemaObject o in xs.Items) {
  329. XmlSchemaElement e = o as XmlSchemaElement;
  330. if (e == null)
  331. continue;
  332. if (e.Name == name)
  333. throw new InvalidOperationException (String.Format (
  334. "Message element named '{0}:{1}' has already been exported.",
  335. ns, name));
  336. }
  337. //Create the element for "parameters"
  338. XmlSchemaElement schema_element = new XmlSchemaElement ();
  339. schema_element.Name = name;
  340. XmlSchemaComplexType complex_type = new XmlSchemaComplexType ();
  341. //Generate Sequence representing the message/parameters
  342. //FIXME: MessageContractAttribute
  343. XmlSchemaSequence sequence = new XmlSchemaSequence ();
  344. XmlSchemaElement element = null;
  345. if (msgbody.ReturnValue == null) {
  346. //parameters
  347. foreach (MessagePartDescription part in msgbody.Parts) {
  348. if (part.Type == null)
  349. //FIXME: Eg. when WsdlImporter is used to import a wsdl
  350. throw new NotImplementedException ();
  351. element = GetSchemaElementForPart (part, xs);
  352. sequence.Items.Add (element);
  353. }
  354. } else {
  355. //ReturnValue
  356. if (msgbody.ReturnValue.Type != typeof (void)) {
  357. element = GetSchemaElementForPart (msgbody.ReturnValue, xs);
  358. sequence.Items.Add (element);
  359. }
  360. }
  361. complex_type.Particle = sequence;
  362. schema_element.SchemaType = complex_type;
  363. xs.Items.Add (schema_element);
  364. GeneratedXmlSchemas.Reprocess (xs);
  365. return new QName (schema_element.Name, xs.TargetNamespace);
  366. }
  367. //Exports <xs:type for SMMessage
  368. //FIXME: complex type for this can be made static
  369. QName ExportTypeMessage ()
  370. {
  371. XmlSchema xs = GetSchema ("http://schemas.microsoft.com/Message");
  372. QName qname = new QName ("MessageBody", xs.TargetNamespace);
  373. foreach (XmlSchemaObject o in xs.Items) {
  374. XmlSchemaComplexType ct = o as XmlSchemaComplexType;
  375. if (ct == null)
  376. continue;
  377. if (ct.Name == "MessageBody")
  378. //Already exported
  379. return qname;
  380. }
  381. XmlSchemaComplexType complex_type = new XmlSchemaComplexType ();
  382. complex_type.Name = "MessageBody";
  383. XmlSchemaSequence sequence = new XmlSchemaSequence ();
  384. XmlSchemaAny any = new XmlSchemaAny ();
  385. any.MinOccurs = 0;
  386. any.MaxOccursString = "unbounded";
  387. any.Namespace = "##any";
  388. sequence.Items.Add (any);
  389. complex_type.Particle = sequence;
  390. xs.Items.Add (complex_type);
  391. GeneratedXmlSchemas.Reprocess (xs);
  392. return qname;
  393. }
  394. XmlSchemaElement GetSchemaElementForPart (MessagePartDescription part, XmlSchema schema)
  395. {
  396. XmlSchemaElement element = new XmlSchemaElement ();
  397. element.Name = part.Name;
  398. XsdExporter.Export (part.Type);
  399. element.SchemaTypeName = XsdExporter.GetSchemaTypeName (part.Type);
  400. AddImport (schema, element.SchemaTypeName.Namespace);
  401. //FIXME: nillable, minOccurs
  402. if (XsdExporter.GetSchemaType (part.Type) is XmlSchemaComplexType ||
  403. part.Type == typeof (string))
  404. element.IsNillable = true;
  405. element.MinOccurs = 0;
  406. return element;
  407. }
  408. //FIXME: Replace with a dictionary ?
  409. void AddImport (XmlSchema schema, string ns)
  410. {
  411. if (ns == XmlSchema.Namespace || schema.TargetNamespace == ns)
  412. return;
  413. foreach (XmlSchemaObject o in schema.Includes) {
  414. XmlSchemaImport import = o as XmlSchemaImport;
  415. if (import == null)
  416. continue;
  417. if (import.Namespace == ns)
  418. return;
  419. }
  420. XmlSchemaImport imp = new XmlSchemaImport ();
  421. imp.Namespace = ns;
  422. schema.Includes.Add (imp);
  423. }
  424. XmlSchema GetSchema (string ns)
  425. {
  426. ICollection colln = GeneratedXmlSchemas.Schemas (ns);
  427. if (colln.Count > 0) {
  428. if (colln.Count > 1)
  429. throw new Exception ("More than 1 schema found for ns = " + ns);
  430. //FIXME: HORRIBLE!
  431. foreach (object o in colln)
  432. return (o as XmlSchema);
  433. }
  434. XmlSchema schema = new XmlSchema ();
  435. schema.TargetNamespace = ns;
  436. schema.ElementFormDefault = XmlSchemaForm.Qualified;
  437. GeneratedXmlSchemas.Add (schema);
  438. return schema;
  439. }
  440. }
  441. }