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.
 
 
 

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