You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

1187 lines
41 KiB

  1. # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
  2. #
  3. # This software is subject to the provisions of the Zope Public License,
  4. # Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
  5. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
  6. # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  7. # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
  8. # FOR A PARTICULAR PURPOSE.
  9. ident = "$Id$"
  10. from Utility import DOM, Collection
  11. from XMLSchema import XMLSchema, SchemaReader, WSDLToolsAdapter
  12. from StringIO import StringIO
  13. import urllib
  14. class WSDLReader:
  15. """A WSDLReader creates WSDL instances from urls and xml data."""
  16. # Custom subclasses of WSDLReader may wish to implement a caching
  17. # strategy or other optimizations. Because application needs vary
  18. # so widely, we don't try to provide any caching by default.
  19. def loadFromStream(self, stream, name=None):
  20. """Return a WSDL instance loaded from a stream object."""
  21. document = DOM.loadDocument(stream)
  22. wsdl = WSDL()
  23. if name:
  24. wsdl.location = name
  25. elif hasattr(stream, 'name'):
  26. wsdl.location = stream.name
  27. wsdl.load(document)
  28. return wsdl
  29. def loadFromURL(self, url):
  30. """Return a WSDL instance loaded from the given url."""
  31. document = DOM.loadFromURL(url)
  32. wsdl = WSDL()
  33. wsdl.location = url
  34. wsdl.load(document)
  35. return wsdl
  36. def loadFromString(self, data):
  37. """Return a WSDL instance loaded from an xml string."""
  38. return self.loadFromStream(StringIO(data))
  39. def loadFromFile(self, filename):
  40. """Return a WSDL instance loaded from the given file."""
  41. file = open(filename, 'rb')
  42. try:
  43. wsdl = self.loadFromStream(file)
  44. finally:
  45. file.close()
  46. return wsdl
  47. class WSDL:
  48. """A WSDL object models a WSDL service description. WSDL objects
  49. may be created manually or loaded from an xml representation
  50. using a WSDLReader instance."""
  51. def __init__(self, targetNamespace=None, strict=1):
  52. self.targetNamespace = targetNamespace or 'urn:this-document.wsdl'
  53. self.documentation = ''
  54. self.location = None
  55. self.document = None
  56. self.name = None
  57. self.services = Collection(self)
  58. self.messages = Collection(self)
  59. self.portTypes = Collection(self)
  60. self.bindings = Collection(self)
  61. self.imports = Collection(self)
  62. self.types = Types(self)
  63. self.extensions = []
  64. self.strict = strict
  65. def __del__(self):
  66. if self.document is not None:
  67. self.document.unlink()
  68. self.document = None
  69. version = '1.1'
  70. def addService(self, name, documentation=''):
  71. if self.services.has_key(name):
  72. raise WSDLError(
  73. 'Duplicate service element: %s' % name
  74. )
  75. item = Service(name, documentation)
  76. self.services[name] = item
  77. return item
  78. def addMessage(self, name, documentation=''):
  79. if self.messages.has_key(name):
  80. raise WSDLError(
  81. 'Duplicate message element: %s.' % name
  82. )
  83. item = Message(name, documentation)
  84. self.messages[name] = item
  85. return item
  86. def addPortType(self, name, documentation=''):
  87. if self.portTypes.has_key(name):
  88. raise WSDLError(
  89. 'Duplicate portType element: name'
  90. )
  91. item = PortType(name, documentation)
  92. self.portTypes[name] = item
  93. return item
  94. def addBinding(self, name, type, documentation=''):
  95. if self.bindings.has_key(name):
  96. raise WSDLError(
  97. 'Duplicate binding element: %s' % name
  98. )
  99. item = Binding(name, type, documentation)
  100. self.bindings[name] = item
  101. return item
  102. def addImport(self, namespace, location):
  103. item = ImportElement(namespace, location)
  104. self.imports[namespace] = item
  105. return item
  106. def load(self, document):
  107. # We save a reference to the DOM document to ensure that elements
  108. # saved as "extensions" will continue to have a meaningful context
  109. # for things like namespace references. The lifetime of the DOM
  110. # document is bound to the lifetime of the WSDL instance.
  111. self.document = document
  112. definitions = DOM.getElement(document, 'definitions', None, None)
  113. if definitions is None:
  114. raise WSDLError(
  115. 'Missing <definitions> element.'
  116. )
  117. self.version = DOM.WSDLUriToVersion(definitions.namespaceURI)
  118. NS_WSDL = DOM.GetWSDLUri(self.version)
  119. self.targetNamespace = DOM.getAttr(definitions, 'targetNamespace',
  120. None, None)
  121. self.name = DOM.getAttr(definitions, 'name', None, None)
  122. self.documentation = GetDocumentation(definitions)
  123. # Resolve (recursively) any import elements in the document.
  124. imported = {}
  125. while 1:
  126. imports = []
  127. for element in DOM.getElements(definitions, 'import', NS_WSDL):
  128. location = DOM.getAttr(element, 'location')
  129. if not imported.has_key(location):
  130. imports.append(element)
  131. if not imports:
  132. break
  133. for element in imports:
  134. self._import(document, element)
  135. location = DOM.getAttr(element, 'location')
  136. imported[location] = 1
  137. reader = SchemaReader(base_url=self.location)
  138. for element in DOM.getElements(definitions, None, None):
  139. localName = element.localName
  140. if not DOM.nsUriMatch(element.namespaceURI, NS_WSDL):
  141. if localName == 'schema':
  142. schema = reader.loadFromNode(WSDLToolsAdapter(self), element)
  143. schema.setBaseUrl(self.location)
  144. self.types.addSchema(schema)
  145. else:
  146. self.extensions.append(element)
  147. continue
  148. elif localName == 'message':
  149. name = DOM.getAttr(element, 'name')
  150. docs = GetDocumentation(element)
  151. message = self.addMessage(name, docs)
  152. parts = DOM.getElements(element, 'part', NS_WSDL)
  153. message.load(parts)
  154. continue
  155. elif localName == 'portType':
  156. name = DOM.getAttr(element, 'name')
  157. docs = GetDocumentation(element)
  158. ptype = self.addPortType(name, docs)
  159. operations = DOM.getElements(element, 'operation', NS_WSDL)
  160. ptype.load(operations)
  161. continue
  162. elif localName == 'binding':
  163. name = DOM.getAttr(element, 'name')
  164. type = DOM.getAttr(element, 'type', default=None)
  165. if type is None:
  166. raise WSDLError(
  167. 'Missing type attribute for binding %s.' % name
  168. )
  169. type = type.split(':', 1)[-1]
  170. docs = GetDocumentation(element)
  171. binding = self.addBinding(name, type, docs)
  172. operations = DOM.getElements(element, 'operation', NS_WSDL)
  173. binding.load(operations)
  174. binding.load_ex(GetExtensions(element))
  175. continue
  176. elif localName == 'service':
  177. name = DOM.getAttr(element, 'name')
  178. docs = GetDocumentation(element)
  179. service = self.addService(name, docs)
  180. ports = DOM.getElements(element, 'port', NS_WSDL)
  181. service.load(ports)
  182. service.load_ex(GetExtensions(element))
  183. continue
  184. elif localName == 'types':
  185. self.types.documentation = GetDocumentation(element)
  186. for item in DOM.getElements(element, None, None):
  187. if item.localName == 'schema':
  188. schema = reader.loadFromNode(WSDLToolsAdapter(self), item)
  189. schema.setBaseUrl(self.location)
  190. self.types.addSchema(schema)
  191. else:
  192. self.types.addExtension(item)
  193. continue
  194. def _import(self, document, element):
  195. namespace = DOM.getAttr(element, 'namespace', default=None)
  196. location = DOM.getAttr(element, 'location', default=None)
  197. if namespace is None or location is None:
  198. raise WSDLError(
  199. 'Invalid import element (missing namespace or location).'
  200. )
  201. # Sort-of support relative locations to simplify unit testing. The
  202. # WSDL specification actually doesn't allow relative URLs, so its
  203. # ok that this only works with urls relative to the initial document.
  204. location = urllib.basejoin(self.location, location)
  205. obimport = self.addImport(namespace, location)
  206. obimport._loaded = 1
  207. importdoc = DOM.loadFromURL(location)
  208. try:
  209. if location.find('#') > -1:
  210. idref = location.split('#')[-1]
  211. imported = DOM.getElementById(importdoc, idref)
  212. else:
  213. imported = importdoc.documentElement
  214. if imported is None:
  215. raise WSDLError(
  216. 'Import target element not found for: %s' % location
  217. )
  218. imported_tns = DOM.findTargetNS(imported)
  219. if imported_tns != namespace:
  220. return
  221. if imported.localName == 'definitions':
  222. imported_nodes = imported.childNodes
  223. else:
  224. imported_nodes = [imported]
  225. parent = element.parentNode
  226. for node in imported_nodes:
  227. if node.nodeType != node.ELEMENT_NODE:
  228. continue
  229. child = DOM.importNode(document, node, 1)
  230. parent.appendChild(child)
  231. child.setAttribute('targetNamespace', namespace)
  232. attrsNS = imported._attrsNS
  233. for attrkey in attrsNS.keys():
  234. if attrkey[0] == DOM.NS_XMLNS:
  235. attr = attrsNS[attrkey].cloneNode(1)
  236. child.setAttributeNode(attr)
  237. finally:
  238. importdoc.unlink()
  239. class Element:
  240. """A class that provides common functions for WSDL element classes."""
  241. def __init__(self, name=None, documentation=''):
  242. self.name = name
  243. self.documentation = documentation
  244. self.extensions = []
  245. def addExtension(self, item):
  246. self.extensions.append(item)
  247. class ImportElement(Element):
  248. def __init__(self, namespace, location):
  249. self.namespace = namespace
  250. self.location = location
  251. _loaded = None
  252. class Types(Collection):
  253. default = lambda self,k: k.targetNamespace
  254. def __init__(self, parent):
  255. Collection.__init__(self, parent)
  256. self.documentation = ''
  257. self.extensions = []
  258. def addSchema(self, schema):
  259. name = schema.targetNamespace
  260. self[name] = schema
  261. return schema
  262. def addExtension(self, item):
  263. self.extensions.append(item)
  264. class Message(Element):
  265. def __init__(self, name, documentation=''):
  266. Element.__init__(self, name, documentation)
  267. self.parts = Collection(self)
  268. def addPart(self, name, type=None, element=None):
  269. if self.parts.has_key(name):
  270. raise WSDLError(
  271. 'Duplicate message part element: %s' % name
  272. )
  273. if type is None and element is None:
  274. raise WSDLError(
  275. 'Missing type or element attribute for part: %s' % name
  276. )
  277. item = MessagePart(name)
  278. item.element = element
  279. item.type = type
  280. self.parts[name] = item
  281. return item
  282. def load(self, elements):
  283. for element in elements:
  284. name = DOM.getAttr(element, 'name')
  285. part = MessagePart(name)
  286. self.parts[name] = part
  287. elemref = DOM.getAttr(element, 'element', default=None)
  288. typeref = DOM.getAttr(element, 'type', default=None)
  289. if typeref is None and elemref is None:
  290. raise WSDLError(
  291. 'No type or element attribute for part: %s' % name
  292. )
  293. if typeref is not None:
  294. part.type = ParseTypeRef(typeref, element)
  295. if elemref is not None:
  296. part.element = ParseTypeRef(elemref, element)
  297. class MessagePart(Element):
  298. def __init__(self, name):
  299. Element.__init__(self, name, '')
  300. self.element = None
  301. self.type = None
  302. class PortType(Element):
  303. def __init__(self, name, documentation=''):
  304. Element.__init__(self, name, documentation)
  305. self.operations = Collection(self)
  306. def getWSDL(self):
  307. return self.parent().parent()
  308. def addOperation(self, name, documentation='', parameterOrder=None):
  309. item = Operation(name, documentation, parameterOrder)
  310. self.operations[name] = item
  311. return item
  312. def load(self, elements):
  313. for element in elements:
  314. name = DOM.getAttr(element, 'name')
  315. docs = GetDocumentation(element)
  316. param_order = DOM.getAttr(element, 'parameterOrder', default=None)
  317. if param_order is not None:
  318. param_order = param_order.split(' ')
  319. operation = self.addOperation(name, docs, param_order)
  320. item = DOM.getElement(element, 'input', None, None)
  321. if item is not None:
  322. name = DOM.getAttr(item, 'name')
  323. docs = GetDocumentation(item)
  324. msgref = DOM.getAttr(item, 'message')
  325. message = msgref.split(':', 1)[-1]
  326. operation.setInput(message, name, docs)
  327. item = DOM.getElement(element, 'output', None, None)
  328. if item is not None:
  329. name = DOM.getAttr(item, 'name')
  330. docs = GetDocumentation(item)
  331. msgref = DOM.getAttr(item, 'message')
  332. message = msgref.split(':', 1)[-1]
  333. operation.setOutput(message, name, docs)
  334. for item in DOM.getElements(element, 'fault', None):
  335. name = DOM.getAttr(item, 'name')
  336. docs = GetDocumentation(item)
  337. msgref = DOM.getAttr(item, 'message')
  338. message = msgref.split(':', 1)[-1]
  339. operation.addFault(message, name, docs)
  340. class Operation(Element):
  341. def __init__(self, name, documentation='', parameterOrder=None):
  342. Element.__init__(self, name, documentation)
  343. self.parameterOrder = parameterOrder
  344. self.faults = Collection(self)
  345. self.input = None
  346. self.output = None
  347. def getPortType(self):
  348. return self.parent().parent()
  349. def getInputMessage(self):
  350. if self.input is None:
  351. return None
  352. wsdl = self.getPortType().getWSDL()
  353. return wsdl.messages[self.input.message]
  354. def getOutputMessage(self):
  355. if self.output is None:
  356. return None
  357. wsdl = self.getPortType().getWSDL()
  358. return wsdl.messages[self.output.message]
  359. def getFaultMessage(self, name):
  360. wsdl = self.getPortType().getWSDL()
  361. return wsdl.messages[self.faults[name].message]
  362. def addFault(self, name, message, documentation=''):
  363. if self.faults.has_key(name):
  364. raise WSDLError(
  365. 'Duplicate fault element: %s' % name
  366. )
  367. item = MessageRole('fault', message, name, documentation)
  368. self.faults[name] = item
  369. return item
  370. def setInput(self, message, name='', documentation=''):
  371. self.input = MessageRole('input', message, name, documentation)
  372. return self.input
  373. def setOutput(self, message, name='', documentation=''):
  374. self.output = MessageRole('output', message, name, documentation)
  375. return self.output
  376. class MessageRole(Element):
  377. def __init__(self, type, message, name='', documentation=''):
  378. Element.__init__(self, name, documentation)
  379. self.message = message
  380. self.type = type
  381. class Binding(Element):
  382. def __init__(self, name, type, documentation=''):
  383. Element.__init__(self, name, documentation)
  384. self.operations = Collection(self)
  385. self.type = type
  386. def getWSDL(self):
  387. """Return the WSDL object that contains this binding."""
  388. return self.parent().parent()
  389. def getPortType(self):
  390. """Return the PortType object associated with this binding."""
  391. return self.getWSDL().portTypes[self.type]
  392. def findBinding(self, kind):
  393. for item in self.extensions:
  394. if isinstance(item, kind):
  395. return item
  396. return None
  397. def findBindings(self, kind):
  398. return [ item for item in self.extensions if isinstance(item, kind) ]
  399. def addOperationBinding(self, name, documentation=''):
  400. item = OperationBinding(name, documentation)
  401. self.operations[name] = item
  402. return item
  403. def load(self, elements):
  404. for element in elements:
  405. name = DOM.getAttr(element, 'name')
  406. docs = GetDocumentation(element)
  407. opbinding = self.addOperationBinding(name, docs)
  408. opbinding.load_ex(GetExtensions(element))
  409. item = DOM.getElement(element, 'input', None, None)
  410. if item is not None:
  411. mbinding = MessageRoleBinding('input')
  412. mbinding.documentation = GetDocumentation(item)
  413. opbinding.input = mbinding
  414. mbinding.load_ex(GetExtensions(item))
  415. item = DOM.getElement(element, 'output', None, None)
  416. if item is not None:
  417. mbinding = MessageRoleBinding('output')
  418. mbinding.documentation = GetDocumentation(item)
  419. opbinding.output = mbinding
  420. mbinding.load_ex(GetExtensions(item))
  421. for item in DOM.getElements(element, 'fault', None):
  422. name = DOM.getAttr(item, 'name')
  423. mbinding = MessageRoleBinding('fault', name)
  424. mbinding.documentation = GetDocumentation(item)
  425. opbinding.faults[name] = mbinding
  426. mbinding.load_ex(GetExtensions(item))
  427. def load_ex(self, elements):
  428. for e in elements:
  429. ns, name = e.namespaceURI, e.localName
  430. if ns in DOM.NS_SOAP_BINDING_ALL and name == 'binding':
  431. transport = DOM.getAttr(e, 'transport', default=None)
  432. style = DOM.getAttr(e, 'style', default='document')
  433. ob = SoapBinding(transport, style)
  434. self.addExtension(ob)
  435. continue
  436. elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'binding':
  437. verb = DOM.getAttr(e, 'verb')
  438. ob = HttpBinding(verb)
  439. self.addExtension(ob)
  440. continue
  441. else:
  442. self.addExtension(e)
  443. class OperationBinding(Element):
  444. def __init__(self, name, documentation=''):
  445. Element.__init__(self, name, documentation)
  446. self.input = None
  447. self.output = None
  448. self.faults = Collection(self)
  449. def getBinding(self):
  450. """Return the parent Binding object of the operation binding."""
  451. return self.parent().parent()
  452. def getOperation(self):
  453. """Return the abstract Operation associated with this binding."""
  454. return self.getBinding().getPortType().operations[self.name]
  455. def findBinding(self, kind):
  456. for item in self.extensions:
  457. if isinstance(item, kind):
  458. return item
  459. return None
  460. def findBindings(self, kind):
  461. return [ item for item in self.extensions if isinstance(item, kind) ]
  462. def addInputBinding(self, binding):
  463. if self.input is None:
  464. self.input = MessageRoleBinding('input')
  465. self.input.addExtension(binding)
  466. return binding
  467. def addOutputBinding(self, binding):
  468. if self.output is None:
  469. self.output = MessageRoleBinding('output')
  470. self.output.addExtension(binding)
  471. return binding
  472. def addFaultBinding(self, name, binding):
  473. fault = self.get(name, None)
  474. if fault is None:
  475. fault = MessageRoleBinding('fault', name)
  476. fault.addExtension(binding)
  477. return binding
  478. def load_ex(self, elements):
  479. for e in elements:
  480. ns, name = e.namespaceURI, e.localName
  481. if ns in DOM.NS_SOAP_BINDING_ALL and name == 'operation':
  482. soapaction = DOM.getAttr(e, 'soapAction', default=None)
  483. style = DOM.getAttr(e, 'style', default=None)
  484. ob = SoapOperationBinding(soapaction, style)
  485. self.addExtension(ob)
  486. continue
  487. elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'operation':
  488. location = DOM.getAttr(e, 'location')
  489. ob = HttpOperationBinding(location)
  490. self.addExtension(ob)
  491. continue
  492. else:
  493. self.addExtension(e)
  494. class MessageRoleBinding(Element):
  495. def __init__(self, type, name='', documentation=''):
  496. Element.__init__(self, name, documentation)
  497. self.type = type
  498. def findBinding(self, kind):
  499. for item in self.extensions:
  500. if isinstance(item, kind):
  501. return item
  502. return None
  503. def findBindings(self, kind):
  504. return [ item for item in self.extensions if isinstance(item, kind) ]
  505. def load_ex(self, elements):
  506. for e in elements:
  507. ns, name = e.namespaceURI, e.localName
  508. if ns in DOM.NS_SOAP_BINDING_ALL and name == 'body':
  509. encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
  510. namespace = DOM.getAttr(e, 'namespace', default=None)
  511. parts = DOM.getAttr(e, 'parts', default=None)
  512. use = DOM.getAttr(e, 'use', default=None)
  513. if use is None:
  514. raise WSDLError(
  515. 'Invalid soap:body binding element.'
  516. )
  517. ob = SoapBodyBinding(use, namespace, encstyle, parts)
  518. self.addExtension(ob)
  519. continue
  520. elif ns in DOM.NS_SOAP_BINDING_ALL and name == 'fault':
  521. encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
  522. namespace = DOM.getAttr(e, 'namespace', default=None)
  523. name = DOM.getAttr(e, 'name', default=None)
  524. use = DOM.getAttr(e, 'use', default=None)
  525. if use is None or name is None:
  526. raise WSDLError(
  527. 'Invalid soap:fault binding element.'
  528. )
  529. ob = SoapFaultBinding(name, use, namespace, encstyle)
  530. self.addExtension(ob)
  531. continue
  532. elif ns in DOM.NS_SOAP_BINDING_ALL and name in (
  533. 'header', 'headerfault'
  534. ):
  535. encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
  536. namespace = DOM.getAttr(e, 'namespace', default=None)
  537. message = DOM.getAttr(e, 'message')
  538. part = DOM.getAttr(e, 'part')
  539. use = DOM.getAttr(e, 'use')
  540. if name == 'header':
  541. _class = SoapHeaderBinding
  542. else:
  543. _class = SoapHeaderFaultBinding
  544. ob = _class(message, part, use, namespace, encstyle)
  545. self.addExtension(ob)
  546. continue
  547. elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'urlReplacement':
  548. ob = HttpUrlReplacementBinding()
  549. self.addExtension(ob)
  550. continue
  551. elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'urlEncoded':
  552. ob = HttpUrlEncodedBinding()
  553. self.addExtension(ob)
  554. continue
  555. elif ns in DOM.NS_MIME_BINDING_ALL and name == 'multipartRelated':
  556. ob = MimeMultipartRelatedBinding()
  557. self.addExtension(ob)
  558. ob.load_ex(GetExtensions(e))
  559. continue
  560. elif ns in DOM.NS_MIME_BINDING_ALL and name == 'content':
  561. part = DOM.getAttr(e, 'part', default=None)
  562. type = DOM.getAttr(e, 'type', default=None)
  563. ob = MimeContentBinding(part, type)
  564. self.addExtension(ob)
  565. continue
  566. elif ns in DOM.NS_MIME_BINDING_ALL and name == 'mimeXml':
  567. part = DOM.getAttr(e, 'part', default=None)
  568. ob = MimeXmlBinding(part)
  569. self.addExtension(ob)
  570. continue
  571. else:
  572. self.addExtension(e)
  573. class Service(Element):
  574. def __init__(self, name, documentation=''):
  575. Element.__init__(self, name, documentation)
  576. self.ports = Collection(self)
  577. def getWSDL(self):
  578. return self.parent().parent()
  579. def addPort(self, name, binding, documentation=''):
  580. item = Port(name, binding, documentation)
  581. self.ports[name] = item
  582. return item
  583. def load(self, elements):
  584. for element in elements:
  585. name = DOM.getAttr(element, 'name', default=None)
  586. docs = GetDocumentation(element)
  587. binding = DOM.getAttr(element, 'binding', default=None)
  588. if name is None or binding is None:
  589. raise WSDLError(
  590. 'Invalid port element.'
  591. )
  592. binding = binding.split(':', 1)[-1]
  593. port = self.addPort(name, binding, docs)
  594. port.load_ex(GetExtensions(element))
  595. def load_ex(self, elements):
  596. for e in elements:
  597. self.addExtension(e)
  598. class Port(Element):
  599. def __init__(self, name, binding, documentation=''):
  600. Element.__init__(self, name, documentation)
  601. self.binding = binding
  602. def getService(self):
  603. """Return the Service object associated with this port."""
  604. return self.parent().parent()
  605. def getBinding(self):
  606. """Return the Binding object that is referenced by this port."""
  607. wsdl = self.getService().getWSDL()
  608. return wsdl.bindings[self.binding]
  609. def getPortType(self):
  610. """Return the PortType object that is referenced by this port."""
  611. wsdl = self.getService().getWSDL()
  612. binding = wsdl.bindings[self.binding]
  613. return wsdl.portTypes[binding.type]
  614. def getAddressBinding(self):
  615. """A convenience method to obtain the extension element used
  616. as the address binding for the port, or None if undefined."""
  617. for item in self.extensions:
  618. if isinstance(item, SoapAddressBinding) or \
  619. isinstance(item, HttpAddressBinding):
  620. return item
  621. raise WSDLError(
  622. 'No address binding found in port.'
  623. )
  624. def load_ex(self, elements):
  625. for e in elements:
  626. ns, name = e.namespaceURI, e.localName
  627. if ns in DOM.NS_SOAP_BINDING_ALL and name == 'address':
  628. location = DOM.getAttr(e, 'location', default=None)
  629. ob = SoapAddressBinding(location)
  630. self.addExtension(ob)
  631. continue
  632. elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'address':
  633. location = DOM.getAttr(e, 'location', default=None)
  634. ob = HttpAddressBinding(location)
  635. self.addExtension(ob)
  636. continue
  637. else:
  638. self.addExtension(e)
  639. class SoapBinding:
  640. def __init__(self, transport, style='rpc'):
  641. self.transport = transport
  642. self.style = style
  643. class SoapAddressBinding:
  644. def __init__(self, location):
  645. self.location = location
  646. class SoapOperationBinding:
  647. def __init__(self, soapAction=None, style=None):
  648. self.soapAction = soapAction
  649. self.style = style
  650. class SoapBodyBinding:
  651. def __init__(self, use, namespace=None, encodingStyle=None, parts=None):
  652. if not use in ('literal', 'encoded'):
  653. raise WSDLError(
  654. 'Invalid use attribute value: %s' % use
  655. )
  656. self.encodingStyle = encodingStyle
  657. self.namespace = namespace
  658. if type(parts) in (type(''), type(u'')):
  659. parts = parts.split()
  660. self.parts = parts
  661. self.use = use
  662. class SoapFaultBinding:
  663. def __init__(self, name, use, namespace=None, encodingStyle=None):
  664. if not use in ('literal', 'encoded'):
  665. raise WSDLError(
  666. 'Invalid use attribute value: %s' % use
  667. )
  668. self.encodingStyle = encodingStyle
  669. self.namespace = namespace
  670. self.name = name
  671. self.use = use
  672. class SoapHeaderBinding:
  673. def __init__(self, message, part, use, namespace=None, encodingStyle=None):
  674. if not use in ('literal', 'encoded'):
  675. raise WSDLError(
  676. 'Invalid use attribute value: %s' % use
  677. )
  678. self.encodingStyle = encodingStyle
  679. self.namespace = namespace
  680. self.message = message
  681. self.part = part
  682. self.use = use
  683. tagname = 'header'
  684. class SoapHeaderFaultBinding(SoapHeaderBinding):
  685. tagname = 'headerfault'
  686. class HttpBinding:
  687. def __init__(self, verb):
  688. self.verb = verb
  689. class HttpAddressBinding:
  690. def __init__(self, location):
  691. self.location = location
  692. class HttpOperationBinding:
  693. def __init__(self, location):
  694. self.location = location
  695. class HttpUrlReplacementBinding:
  696. pass
  697. class HttpUrlEncodedBinding:
  698. pass
  699. class MimeContentBinding:
  700. def __init__(self, part=None, type=None):
  701. self.part = part
  702. self.type = type
  703. class MimeXmlBinding:
  704. def __init__(self, part=None):
  705. self.part = part
  706. class MimeMultipartRelatedBinding:
  707. def __init__(self):
  708. self.parts = []
  709. def load_ex(self, elements):
  710. for e in elements:
  711. ns, name = e.namespaceURI, e.localName
  712. if ns in DOM.NS_MIME_BINDING_ALL and name == 'part':
  713. self.parts.append(MimePartBinding())
  714. continue
  715. class MimePartBinding:
  716. def __init__(self):
  717. self.items = []
  718. def load_ex(self, elements):
  719. for e in elements:
  720. ns, name = e.namespaceURI, e.localName
  721. if ns in DOM.NS_MIME_BINDING_ALL and name == 'content':
  722. part = DOM.getAttr(e, 'part', default=None)
  723. type = DOM.getAttr(e, 'type', default=None)
  724. ob = MimeContentBinding(part, type)
  725. self.items.append(ob)
  726. continue
  727. elif ns in DOM.NS_MIME_BINDING_ALL and name == 'mimeXml':
  728. part = DOM.getAttr(e, 'part', default=None)
  729. ob = MimeXmlBinding(part)
  730. self.items.append(ob)
  731. continue
  732. elif ns in DOM.NS_SOAP_BINDING_ALL and name == 'body':
  733. encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
  734. namespace = DOM.getAttr(e, 'namespace', default=None)
  735. parts = DOM.getAttr(e, 'parts', default=None)
  736. use = DOM.getAttr(e, 'use', default=None)
  737. if use is None:
  738. raise WSDLError(
  739. 'Invalid soap:body binding element.'
  740. )
  741. ob = SoapBodyBinding(use, namespace, encstyle, parts)
  742. self.items.append(ob)
  743. continue
  744. class WSDLError(Exception):
  745. pass
  746. def DeclareNSPrefix(writer, prefix, nsuri):
  747. if writer.hasNSPrefix(nsuri):
  748. return
  749. writer.declareNSPrefix(prefix, nsuri)
  750. def ParseTypeRef(value, element):
  751. parts = value.split(':', 1)
  752. if len(parts) == 1:
  753. return (DOM.findTargetNS(element), value)
  754. nsuri = DOM.findNamespaceURI(parts[0], element)
  755. return (nsuri, parts[1])
  756. def ParseQName(value, element):
  757. nameref = value.split(':', 1)
  758. if len(nameref) == 2:
  759. nsuri = DOM.findNamespaceURI(nameref[0], element)
  760. name = nameref[-1]
  761. else:
  762. nsuri = DOM.findTargetNS(element)
  763. name = nameref[-1]
  764. return nsuri, name
  765. def GetDocumentation(element):
  766. docnode = DOM.getElement(element, 'documentation', None, None)
  767. if docnode is not None:
  768. return DOM.getElementText(docnode)
  769. return ''
  770. def GetExtensions(element):
  771. return [ item for item in DOM.getElements(element, None, None)
  772. if item.namespaceURI != DOM.NS_WSDL ]
  773. def FindExtensions(object, kind, t_type=type(())):
  774. if isinstance(kind, t_type):
  775. result = []
  776. namespaceURI, name = kind
  777. return [ item for item in object.extensions
  778. if hasattr(item, 'nodeType') \
  779. and DOM.nsUriMatch(namespaceURI, item.namespaceURI) \
  780. and item.name == name ]
  781. return [ item for item in object.extensions if isinstance(item, kind) ]
  782. def FindExtension(object, kind, t_type=type(())):
  783. if isinstance(kind, t_type):
  784. namespaceURI, name = kind
  785. for item in object.extensions:
  786. if hasattr(item, 'nodeType') \
  787. and DOM.nsUriMatch(namespaceURI, item.namespaceURI) \
  788. and item.name == name:
  789. return item
  790. else:
  791. for item in object.extensions:
  792. if isinstance(item, kind):
  793. return item
  794. return None
  795. class SOAPCallInfo:
  796. """SOAPCallInfo captures the important binding information about a
  797. SOAP operation, in a structure that is easier to work with than
  798. raw WSDL structures."""
  799. def __init__(self, methodName):
  800. self.methodName = methodName
  801. self.inheaders = []
  802. self.outheaders = []
  803. self.inparams = []
  804. self.outparams = []
  805. self.retval = None
  806. encodingStyle = DOM.NS_SOAP_ENC
  807. documentation = ''
  808. soapAction = None
  809. transport = None
  810. namespace = None
  811. location = None
  812. use = 'encoded'
  813. style = 'rpc'
  814. def addInParameter(self, name, type, namespace=None, element_type=0):
  815. """Add an input parameter description to the call info."""
  816. parameter = ParameterInfo(name, type, namespace, element_type)
  817. self.inparams.append(parameter)
  818. return parameter
  819. def addOutParameter(self, name, type, namespace=None, element_type=0):
  820. """Add an output parameter description to the call info."""
  821. parameter = ParameterInfo(name, type, namespace, element_type)
  822. self.outparams.append(parameter)
  823. return parameter
  824. def setReturnParameter(self, name, type, namespace=None, element_type=0):
  825. """Set the return parameter description for the call info."""
  826. parameter = ParameterInfo(name, type, namespace, element_type)
  827. self.retval = parameter
  828. return parameter
  829. def addInHeaderInfo(self, name, type, namespace, element_type=0,
  830. mustUnderstand=0):
  831. """Add an input SOAP header description to the call info."""
  832. headerinfo = HeaderInfo(name, type, namespace, element_type)
  833. if mustUnderstand:
  834. headerinfo.mustUnderstand = 1
  835. self.inheaders.append(headerinfo)
  836. return headerinfo
  837. def addOutHeaderInfo(self, name, type, namespace, element_type=0,
  838. mustUnderstand=0):
  839. """Add an output SOAP header description to the call info."""
  840. headerinfo = HeaderInfo(name, type, namespace, element_type)
  841. if mustUnderstand:
  842. headerinfo.mustUnderstand = 1
  843. self.outheaders.append(headerinfo)
  844. return headerinfo
  845. def getInParameters(self):
  846. """Return a sequence of the in parameters of the method."""
  847. return self.inparams
  848. def getOutParameters(self):
  849. """Return a sequence of the out parameters of the method."""
  850. return self.outparams
  851. def getReturnParameter(self):
  852. """Return param info about the return value of the method."""
  853. return self.retval
  854. def getInHeaders(self):
  855. """Return a sequence of the in headers of the method."""
  856. return self.inheaders
  857. def getOutHeaders(self):
  858. """Return a sequence of the out headers of the method."""
  859. return self.outheaders
  860. class ParameterInfo:
  861. """A ParameterInfo object captures parameter binding information."""
  862. def __init__(self, name, type, namespace=None, element_type=0):
  863. if element_type:
  864. self.element_type = 1
  865. if namespace is not None:
  866. self.namespace = namespace
  867. self.name = name
  868. self.type = type
  869. element_type = 0
  870. namespace = None
  871. default = None
  872. class HeaderInfo(ParameterInfo):
  873. """A HeaderInfo object captures SOAP header binding information."""
  874. def __init__(self, name, type, namespace, element_type=None):
  875. ParameterInfo.__init__(self, name, type, namespace, element_type)
  876. mustUnderstand = 0
  877. actor = None
  878. def callInfoFromWSDL(port, name):
  879. """Return a SOAPCallInfo given a WSDL port and operation name."""
  880. wsdl = port.getService().getWSDL()
  881. binding = port.getBinding()
  882. portType = binding.getPortType()
  883. operation = portType.operations[name]
  884. opbinding = binding.operations[name]
  885. messages = wsdl.messages
  886. callinfo = SOAPCallInfo(name)
  887. addrbinding = port.getAddressBinding()
  888. if not isinstance(addrbinding, SoapAddressBinding):
  889. raise ValueError, 'Unsupported binding type.'
  890. callinfo.location = addrbinding.location
  891. soapbinding = binding.findBinding(SoapBinding)
  892. if soapbinding is None:
  893. raise ValueError, 'Missing soap:binding element.'
  894. callinfo.transport = soapbinding.transport
  895. callinfo.style = soapbinding.style or 'document'
  896. soap_op_binding = opbinding.findBinding(SoapOperationBinding)
  897. if soap_op_binding is not None:
  898. callinfo.soapAction = soap_op_binding.soapAction
  899. callinfo.style = soap_op_binding.style or callinfo.style
  900. parameterOrder = operation.parameterOrder
  901. if operation.input is not None:
  902. message = messages[operation.input.message]
  903. msgrole = opbinding.input
  904. mime = msgrole.findBinding(MimeMultipartRelatedBinding)
  905. if mime is not None:
  906. raise ValueError, 'Mime bindings are not supported.'
  907. else:
  908. for item in msgrole.findBindings(SoapHeaderBinding):
  909. part = messages[item.message].parts[item.part]
  910. header = callinfo.addInHeaderInfo(
  911. part.name,
  912. part.element or part.type,
  913. item.namespace,
  914. element_type = part.element and 1 or 0
  915. )
  916. header.encodingStyle = item.encodingStyle
  917. body = msgrole.findBinding(SoapBodyBinding)
  918. if body is None:
  919. raise ValueError, 'Missing soap:body binding.'
  920. callinfo.encodingStyle = body.encodingStyle
  921. callinfo.namespace = body.namespace
  922. callinfo.use = body.use
  923. if body.parts is not None:
  924. parts = []
  925. for name in body.parts:
  926. parts.append(message.parts[name])
  927. else:
  928. parts = message.parts.values()
  929. for part in parts:
  930. callinfo.addInParameter(
  931. part.name,
  932. part.element or part.type,
  933. element_type = part.element and 1 or 0
  934. )
  935. if operation.output is not None:
  936. try:
  937. message = messages[operation.output.message]
  938. except KeyError:
  939. if self.strict:
  940. raise RuntimeError(
  941. "Recieved message not defined in the WSDL schema: %s" %
  942. operation.output.message)
  943. else:
  944. message = wsdl.addMessage(operation.output.message)
  945. print "Warning:", \
  946. "Recieved message not defined in the WSDL schema.", \
  947. "Adding it."
  948. print "Message:", operation.output.message
  949. msgrole = opbinding.output
  950. mime = msgrole.findBinding(MimeMultipartRelatedBinding)
  951. if mime is not None:
  952. raise ValueError, 'Mime bindings are not supported.'
  953. else:
  954. for item in msgrole.findBindings(SoapHeaderBinding):
  955. part = messages[item.message].parts[item.part]
  956. header = callinfo.addOutHeaderInfo(
  957. part.name,
  958. part.element or part.type,
  959. item.namespace,
  960. element_type = part.element and 1 or 0
  961. )
  962. header.encodingStyle = item.encodingStyle
  963. body = msgrole.findBinding(SoapBodyBinding)
  964. if body is None:
  965. raise ValueError, 'Missing soap:body binding.'
  966. callinfo.encodingStyle = body.encodingStyle
  967. callinfo.namespace = body.namespace
  968. callinfo.use = body.use
  969. if body.parts is not None:
  970. parts = []
  971. for name in body.parts:
  972. parts.append(message.parts[name])
  973. else:
  974. parts = message.parts.values()
  975. if parts:
  976. callinfo.setReturnParameter(
  977. parts[0].name,
  978. parts[0].element or parts[0].type,
  979. element_type = parts[0].element and 1 or 0
  980. )
  981. for part in parts[1:]:
  982. callinfo.addOutParameter(
  983. part.name,
  984. part.element or part.type,
  985. element_type = part.element and 1 or 0
  986. )
  987. return callinfo