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.
 
 
 

1337 lines
48 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 Namespaces import WSR, WSA
  13. from StringIO import StringIO
  14. import urllib
  15. class WSDLReader:
  16. """A WSDLReader creates WSDL instances from urls and xml data."""
  17. # Custom subclasses of WSDLReader may wish to implement a caching
  18. # strategy or other optimizations. Because application needs vary
  19. # so widely, we don't try to provide any caching by default.
  20. def loadFromStream(self, stream, name=None):
  21. """Return a WSDL instance loaded from a stream object."""
  22. document = DOM.loadDocument(stream)
  23. wsdl = WSDL()
  24. if name:
  25. wsdl.location = name
  26. elif hasattr(stream, 'name'):
  27. wsdl.location = stream.name
  28. wsdl.load(document)
  29. return wsdl
  30. def loadFromURL(self, url):
  31. """Return a WSDL instance loaded from the given url."""
  32. document = DOM.loadFromURL(url)
  33. wsdl = WSDL()
  34. wsdl.location = url
  35. wsdl.load(document)
  36. return wsdl
  37. def loadFromString(self, data):
  38. """Return a WSDL instance loaded from an xml string."""
  39. return self.loadFromStream(StringIO(data))
  40. def loadFromFile(self, filename):
  41. """Return a WSDL instance loaded from the given file."""
  42. file = open(filename, 'rb')
  43. try:
  44. wsdl = self.loadFromStream(file)
  45. finally:
  46. file.close()
  47. return wsdl
  48. class WSDL:
  49. """A WSDL object models a WSDL service description. WSDL objects
  50. may be created manually or loaded from an xml representation
  51. using a WSDLReader instance."""
  52. def __init__(self, targetNamespace=None, strict=1):
  53. self.targetNamespace = targetNamespace or 'urn:this-document.wsdl'
  54. self.documentation = ''
  55. self.location = None
  56. self.document = None
  57. self.name = None
  58. self.services = CollectionNS(self)
  59. self.messages = CollectionNS(self)
  60. self.portTypes = CollectionNS(self)
  61. self.bindings = CollectionNS(self)
  62. #self.imports = Collection(self)
  63. self.types = Types(self)
  64. self.extensions = []
  65. self.strict = strict
  66. def __del__(self):
  67. if self.document is not None:
  68. self.document.unlink()
  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. base_location = self.location
  134. while 1:
  135. #XXX
  136. imports = []
  137. for element in DOM.getElements(definitions, 'import', NS_WSDL):
  138. location = DOM.getAttr(element, 'location')
  139. # Resolve relative location, and save
  140. location = urllib.basejoin(base_location, location)
  141. if not imported.has_key(location):
  142. imports.append(element)
  143. if not imports:
  144. break
  145. for element in imports:
  146. location = DOM.getAttr(element, 'location')
  147. self._import(document, element, base_location)
  148. location = urllib.basejoin(base_location, location)
  149. imported[location] = 1
  150. base_location = ''
  151. #reader = SchemaReader(base_url=self.location)
  152. for element in DOM.getElements(definitions, None, None):
  153. targetNamespace = DOM.getAttr(element, 'targetNamespace')
  154. localName = element.localName
  155. if not DOM.nsUriMatch(element.namespaceURI, NS_WSDL):
  156. if localName == 'schema':
  157. reader = SchemaReader(base_url=self.location)
  158. schema = reader.loadFromNode(WSDLToolsAdapter(self), element)
  159. schema.setBaseUrl(self.location)
  160. self.types.addSchema(schema)
  161. else:
  162. self.extensions.append(element)
  163. continue
  164. elif localName == 'message':
  165. name = DOM.getAttr(element, 'name')
  166. docs = GetDocumentation(element)
  167. message = self.addMessage(name, docs, targetNamespace)
  168. parts = DOM.getElements(element, 'part', NS_WSDL)
  169. message.load(parts)
  170. continue
  171. elif localName == 'portType':
  172. name = DOM.getAttr(element, 'name')
  173. docs = GetDocumentation(element)
  174. ptype = self.addPortType(name, docs, targetNamespace)
  175. #operations = DOM.getElements(element, 'operation', NS_WSDL)
  176. #ptype.load(operations)
  177. ptype.load(element)
  178. continue
  179. elif localName == 'binding':
  180. name = DOM.getAttr(element, 'name')
  181. type = DOM.getAttr(element, 'type', default=None)
  182. if type is None:
  183. raise WSDLError(
  184. 'Missing type attribute for binding %s.' % name
  185. )
  186. type = ParseQName(type, element)
  187. docs = GetDocumentation(element)
  188. binding = self.addBinding(name, type, docs, targetNamespace)
  189. operations = DOM.getElements(element, 'operation', NS_WSDL)
  190. binding.load(operations)
  191. binding.load_ex(GetExtensions(element))
  192. continue
  193. elif localName == 'service':
  194. name = DOM.getAttr(element, 'name')
  195. docs = GetDocumentation(element)
  196. service = self.addService(name, docs, targetNamespace)
  197. ports = DOM.getElements(element, 'port', NS_WSDL)
  198. service.load(ports)
  199. service.load_ex(GetExtensions(element))
  200. continue
  201. elif localName == 'types':
  202. self.types.documentation = GetDocumentation(element)
  203. base_location = DOM.getAttr(element, 'base-location')
  204. if base_location:
  205. element.removeAttribute('base-location')
  206. base_location = base_location or self.location
  207. reader = SchemaReader(base_url=base_location)
  208. for item in DOM.getElements(element, None, None):
  209. if item.localName == 'schema':
  210. schema = reader.loadFromNode(WSDLToolsAdapter(self), item)
  211. # XXX <types> could have been imported
  212. #schema.setBaseUrl(self.location)
  213. schema.setBaseUrl(base_location)
  214. self.types.addSchema(schema)
  215. else:
  216. self.types.addExtension(item)
  217. # XXX remove the attribute
  218. # element.removeAttribute('base-location')
  219. continue
  220. def _import(self, document, element, base_location=None):
  221. '''Algo take <import> element's children, clone them,
  222. and add them to the main document. Support for relative
  223. locations is a bit complicated. The orig document context
  224. is lost, so we need to store base location in DOM elements
  225. representing <types>, by creating a special temporary
  226. "base-location" attribute, and <import>, by resolving
  227. the relative "location" and storing it as "location".
  228. document -- document we are loading
  229. element -- DOM Element representing <import>
  230. base_location -- location of document from which this
  231. <import> was gleaned.
  232. '''
  233. namespace = DOM.getAttr(element, 'namespace', default=None)
  234. location = DOM.getAttr(element, 'location', default=None)
  235. if namespace is None or location is None:
  236. raise WSDLError(
  237. 'Invalid import element (missing namespace or location).'
  238. )
  239. if base_location:
  240. location = urllib.basejoin(base_location, location)
  241. element.setAttributeNS(None, 'location', location)
  242. #location = urllib.basejoin(self.location, location)
  243. #obimport = self.addImport(namespace, location)
  244. #obimport._loaded = 1
  245. importdoc = DOM.loadFromURL(location)
  246. try:
  247. if location.find('#') > -1:
  248. idref = location.split('#')[-1]
  249. imported = DOM.getElementById(importdoc, idref)
  250. else:
  251. imported = importdoc.documentElement
  252. if imported is None:
  253. raise WSDLError(
  254. 'Import target element not found for: %s' % location
  255. )
  256. imported_tns = DOM.findTargetNS(imported)
  257. if imported_tns != namespace:
  258. return
  259. if imported.localName == 'definitions':
  260. imported_nodes = imported.childNodes
  261. else:
  262. imported_nodes = [imported]
  263. parent = element.parentNode
  264. for node in imported_nodes:
  265. if node.nodeType != node.ELEMENT_NODE:
  266. continue
  267. child = DOM.importNode(document, node, 1)
  268. parent.appendChild(child)
  269. child.setAttribute('targetNamespace', namespace)
  270. attrsNS = imported._attrsNS
  271. for attrkey in attrsNS.keys():
  272. if attrkey[0] == DOM.NS_XMLNS:
  273. attr = attrsNS[attrkey].cloneNode(1)
  274. child.setAttributeNode(attr)
  275. #XXX Quick Hack, should be in WSDL Namespace.
  276. if child.localName == 'import':
  277. rlocation = child.getAttributeNS(None, 'location')
  278. alocation = urllib.basejoin(location, rlocation)
  279. child.setAttribute('location', alocation)
  280. elif child.localName == 'types':
  281. child.setAttribute('base-location', location)
  282. finally:
  283. importdoc.unlink()
  284. return location
  285. class Element:
  286. """A class that provides common functions for WSDL element classes."""
  287. def __init__(self, name=None, documentation=''):
  288. self.name = name
  289. self.documentation = documentation
  290. self.extensions = []
  291. def addExtension(self, item):
  292. self.extensions.append(item)
  293. class ImportElement(Element):
  294. def __init__(self, namespace, location):
  295. self.namespace = namespace
  296. self.location = location
  297. _loaded = None
  298. class Types(Collection):
  299. default = lambda self,k: k.targetNamespace
  300. def __init__(self, parent):
  301. Collection.__init__(self, parent)
  302. self.documentation = ''
  303. self.extensions = []
  304. def addSchema(self, schema):
  305. name = schema.targetNamespace
  306. self[name] = schema
  307. return schema
  308. def addExtension(self, item):
  309. self.extensions.append(item)
  310. class Message(Element):
  311. def __init__(self, name, documentation=''):
  312. Element.__init__(self, name, documentation)
  313. self.parts = Collection(self)
  314. def addPart(self, name, type=None, element=None):
  315. if self.parts.has_key(name):
  316. raise WSDLError(
  317. 'Duplicate message part element: %s' % name
  318. )
  319. if type is None and element is None:
  320. raise WSDLError(
  321. 'Missing type or element attribute for part: %s' % name
  322. )
  323. item = MessagePart(name)
  324. item.element = element
  325. item.type = type
  326. self.parts[name] = item
  327. return item
  328. def load(self, elements):
  329. for element in elements:
  330. name = DOM.getAttr(element, 'name')
  331. part = MessagePart(name)
  332. self.parts[name] = part
  333. elemref = DOM.getAttr(element, 'element', default=None)
  334. typeref = DOM.getAttr(element, 'type', default=None)
  335. if typeref is None and elemref is None:
  336. raise WSDLError(
  337. 'No type or element attribute for part: %s' % name
  338. )
  339. if typeref is not None:
  340. part.type = ParseTypeRef(typeref, element)
  341. if elemref is not None:
  342. part.element = ParseTypeRef(elemref, element)
  343. class MessagePart(Element):
  344. def __init__(self, name):
  345. Element.__init__(self, name, '')
  346. self.element = None
  347. self.type = None
  348. def getWSDL(self):
  349. """Return the WSDL object that contains this Message Part."""
  350. return self.parent().parent().parent().parent()
  351. def getTypeDefinition(self):
  352. wsdl = self.getWSDL()
  353. nsuri,name = self.type
  354. schema = wsdl.types.get(nsuri, {})
  355. return schema.get(name)
  356. def getElementDeclaration(self):
  357. wsdl = self.getWSDL()
  358. nsuri,name = self.element
  359. schema = wsdl.types.get(nsuri, {})
  360. return schema.get(name)
  361. class PortType(Element):
  362. '''PortType has a anyAttribute, thus must provide for an extensible
  363. mechanism for supporting such attributes. ResourceProperties is
  364. specified in WS-ResourceProperties. wsa:Action is specified in
  365. WS-Address.
  366. Instance Data:
  367. name -- name attribute
  368. resourceProperties -- optional. wsr:ResourceProperties attribute,
  369. value is a QName this is Parsed into a (namespaceURI, name)
  370. that represents a Global Element Declaration.
  371. operations
  372. '''
  373. def __init__(self, name, documentation=''):
  374. Element.__init__(self, name, documentation)
  375. self.operations = Collection(self)
  376. self.resourceProperties = None
  377. def getWSDL(self):
  378. return self.parent().parent()
  379. def getResourceProperties(self):
  380. return self.resourceProperties
  381. def addOperation(self, name, documentation='', parameterOrder=None):
  382. item = Operation(name, documentation, parameterOrder)
  383. self.operations[name] = item
  384. return item
  385. def load(self, element):
  386. self.name = DOM.getAttr(element, 'name')
  387. self.documentation = GetDocumentation(element)
  388. if DOM.hasAttr(element, 'ResourceProperties', WSR.PROPERTIES):
  389. rpref = DOM.getAttr(element, 'ResourceProperties', WSR.PROPERTIES)
  390. self.resourceProperties = ParseQName(rpref, element)
  391. NS_WSDL = DOM.GetWSDLUri(self.getWSDL().version)
  392. elements = DOM.getElements(element, 'operation', NS_WSDL)
  393. for element in elements:
  394. name = DOM.getAttr(element, 'name')
  395. docs = GetDocumentation(element)
  396. param_order = DOM.getAttr(element, 'parameterOrder', default=None)
  397. if param_order is not None:
  398. param_order = param_order.split(' ')
  399. operation = self.addOperation(name, docs, param_order)
  400. item = DOM.getElement(element, 'input', None, None)
  401. if item is not None:
  402. name = DOM.getAttr(item, 'name')
  403. docs = GetDocumentation(item)
  404. msgref = DOM.getAttr(item, 'message')
  405. message = ParseQName(msgref, item)
  406. action = DOM.getAttr(item, 'Action', WSA.ADDRESS, None)
  407. operation.setInput(message, name, docs, action)
  408. item = DOM.getElement(element, 'output', None, None)
  409. if item is not None:
  410. name = DOM.getAttr(item, 'name')
  411. docs = GetDocumentation(item)
  412. msgref = DOM.getAttr(item, 'message')
  413. message = ParseQName(msgref, item)
  414. action = DOM.getAttr(item, 'Action', WSA.ADDRESS, None)
  415. operation.setOutput(message, name, docs, action)
  416. for item in DOM.getElements(element, 'fault', None):
  417. name = DOM.getAttr(item, 'name')
  418. docs = GetDocumentation(item)
  419. msgref = DOM.getAttr(item, 'message')
  420. message = ParseQName(msgref, item)
  421. action = DOM.getAttr(item, 'Action', WSA.ADDRESS, None)
  422. operation.addFault(message, name, docs, action)
  423. class Operation(Element):
  424. def __init__(self, name, documentation='', parameterOrder=None):
  425. Element.__init__(self, name, documentation)
  426. self.parameterOrder = parameterOrder
  427. self.faults = Collection(self)
  428. self.input = None
  429. self.output = None
  430. def getPortType(self):
  431. return self.parent().parent()
  432. def getInputAction(self):
  433. """wsa:Action attribute"""
  434. return GetWSAActionInput(self)
  435. def getInputMessage(self):
  436. if self.input is None:
  437. return None
  438. wsdl = self.getPortType().getWSDL()
  439. return wsdl.messages[self.input.message]
  440. def getOutputAction(self):
  441. """wsa:Action attribute"""
  442. return GetWSAActionOutput(self)
  443. def getOutputMessage(self):
  444. if self.output is None:
  445. return None
  446. wsdl = self.getPortType().getWSDL()
  447. return wsdl.messages[self.output.message]
  448. def getFaultAction(self, name):
  449. """wsa:Action attribute"""
  450. return GetWSAActionFault(self, name)
  451. def getFaultMessage(self, name):
  452. wsdl = self.getPortType().getWSDL()
  453. return wsdl.messages[self.faults[name].message]
  454. def addFault(self, message, name, documentation='', action=None):
  455. if self.faults.has_key(name):
  456. raise WSDLError(
  457. 'Duplicate fault element: %s' % name
  458. )
  459. item = MessageRole('fault', message, name, documentation, action)
  460. self.faults[name] = item
  461. return item
  462. def setInput(self, message, name='', documentation='', action=None):
  463. self.input = MessageRole('input', message, name, documentation, action)
  464. return self.input
  465. def setOutput(self, message, name='', documentation='', action=None):
  466. self.output = MessageRole('output', message, name, documentation, action)
  467. return self.output
  468. class MessageRole(Element):
  469. def __init__(self, type, message, name='', documentation='', action=None):
  470. Element.__init__(self, name, documentation)
  471. self.message = message
  472. self.type = type
  473. self.action = action
  474. class Binding(Element):
  475. def __init__(self, name, type, documentation=''):
  476. Element.__init__(self, name, documentation)
  477. self.operations = Collection(self)
  478. self.type = type
  479. def getWSDL(self):
  480. """Return the WSDL object that contains this binding."""
  481. return self.parent().parent()
  482. def getPortType(self):
  483. """Return the PortType object associated with this binding."""
  484. return self.getWSDL().portTypes[self.type]
  485. def findBinding(self, kind):
  486. for item in self.extensions:
  487. if isinstance(item, kind):
  488. return item
  489. return None
  490. def findBindings(self, kind):
  491. return [ item for item in self.extensions if isinstance(item, kind) ]
  492. def addOperationBinding(self, name, documentation=''):
  493. item = OperationBinding(name, documentation)
  494. self.operations[name] = item
  495. return item
  496. def load(self, elements):
  497. for element in elements:
  498. name = DOM.getAttr(element, 'name')
  499. docs = GetDocumentation(element)
  500. opbinding = self.addOperationBinding(name, docs)
  501. opbinding.load_ex(GetExtensions(element))
  502. item = DOM.getElement(element, 'input', None, None)
  503. if item is not None:
  504. mbinding = MessageRoleBinding('input')
  505. mbinding.documentation = GetDocumentation(item)
  506. opbinding.input = mbinding
  507. mbinding.load_ex(GetExtensions(item))
  508. item = DOM.getElement(element, 'output', None, None)
  509. if item is not None:
  510. mbinding = MessageRoleBinding('output')
  511. mbinding.documentation = GetDocumentation(item)
  512. opbinding.output = mbinding
  513. mbinding.load_ex(GetExtensions(item))
  514. for item in DOM.getElements(element, 'fault', None):
  515. name = DOM.getAttr(item, 'name')
  516. mbinding = MessageRoleBinding('fault', name)
  517. mbinding.documentation = GetDocumentation(item)
  518. opbinding.faults[name] = mbinding
  519. mbinding.load_ex(GetExtensions(item))
  520. def load_ex(self, elements):
  521. for e in elements:
  522. ns, name = e.namespaceURI, e.localName
  523. if ns in DOM.NS_SOAP_BINDING_ALL and name == 'binding':
  524. transport = DOM.getAttr(e, 'transport', default=None)
  525. style = DOM.getAttr(e, 'style', default='document')
  526. ob = SoapBinding(transport, style)
  527. self.addExtension(ob)
  528. continue
  529. elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'binding':
  530. verb = DOM.getAttr(e, 'verb')
  531. ob = HttpBinding(verb)
  532. self.addExtension(ob)
  533. continue
  534. else:
  535. self.addExtension(e)
  536. class OperationBinding(Element):
  537. def __init__(self, name, documentation=''):
  538. Element.__init__(self, name, documentation)
  539. self.input = None
  540. self.output = None
  541. self.faults = Collection(self)
  542. def getBinding(self):
  543. """Return the parent Binding object of the operation binding."""
  544. return self.parent().parent()
  545. def getOperation(self):
  546. """Return the abstract Operation associated with this binding."""
  547. return self.getBinding().getPortType().operations[self.name]
  548. def findBinding(self, kind):
  549. for item in self.extensions:
  550. if isinstance(item, kind):
  551. return item
  552. return None
  553. def findBindings(self, kind):
  554. return [ item for item in self.extensions if isinstance(item, kind) ]
  555. def addInputBinding(self, binding):
  556. if self.input is None:
  557. self.input = MessageRoleBinding('input')
  558. self.input.addExtension(binding)
  559. return binding
  560. def addOutputBinding(self, binding):
  561. if self.output is None:
  562. self.output = MessageRoleBinding('output')
  563. self.output.addExtension(binding)
  564. return binding
  565. def addFaultBinding(self, name, binding):
  566. fault = self.get(name, None)
  567. if fault is None:
  568. fault = MessageRoleBinding('fault', name)
  569. fault.addExtension(binding)
  570. return binding
  571. def load_ex(self, elements):
  572. for e in elements:
  573. ns, name = e.namespaceURI, e.localName
  574. if ns in DOM.NS_SOAP_BINDING_ALL and name == 'operation':
  575. soapaction = DOM.getAttr(e, 'soapAction', default=None)
  576. style = DOM.getAttr(e, 'style', default=None)
  577. ob = SoapOperationBinding(soapaction, style)
  578. self.addExtension(ob)
  579. continue
  580. elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'operation':
  581. location = DOM.getAttr(e, 'location')
  582. ob = HttpOperationBinding(location)
  583. self.addExtension(ob)
  584. continue
  585. else:
  586. self.addExtension(e)
  587. class MessageRoleBinding(Element):
  588. def __init__(self, type, name='', documentation=''):
  589. Element.__init__(self, name, documentation)
  590. self.type = type
  591. def findBinding(self, kind):
  592. for item in self.extensions:
  593. if isinstance(item, kind):
  594. return item
  595. return None
  596. def findBindings(self, kind):
  597. return [ item for item in self.extensions if isinstance(item, kind) ]
  598. def load_ex(self, elements):
  599. for e in elements:
  600. ns, name = e.namespaceURI, e.localName
  601. if ns in DOM.NS_SOAP_BINDING_ALL and name == 'body':
  602. encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
  603. namespace = DOM.getAttr(e, 'namespace', default=None)
  604. parts = DOM.getAttr(e, 'parts', default=None)
  605. use = DOM.getAttr(e, 'use', default=None)
  606. if use is None:
  607. raise WSDLError(
  608. 'Invalid soap:body binding element.'
  609. )
  610. ob = SoapBodyBinding(use, namespace, encstyle, parts)
  611. self.addExtension(ob)
  612. continue
  613. elif ns in DOM.NS_SOAP_BINDING_ALL and name == 'fault':
  614. encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
  615. namespace = DOM.getAttr(e, 'namespace', default=None)
  616. name = DOM.getAttr(e, 'name', default=None)
  617. use = DOM.getAttr(e, 'use', default=None)
  618. if use is None or name is None:
  619. raise WSDLError(
  620. 'Invalid soap:fault binding element.'
  621. )
  622. ob = SoapFaultBinding(name, use, namespace, encstyle)
  623. self.addExtension(ob)
  624. continue
  625. elif ns in DOM.NS_SOAP_BINDING_ALL and name in (
  626. 'header', 'headerfault'
  627. ):
  628. encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
  629. namespace = DOM.getAttr(e, 'namespace', default=None)
  630. message = DOM.getAttr(e, 'message')
  631. part = DOM.getAttr(e, 'part')
  632. use = DOM.getAttr(e, 'use')
  633. if name == 'header':
  634. _class = SoapHeaderBinding
  635. else:
  636. _class = SoapHeaderFaultBinding
  637. message = ParseQName(message, e)
  638. ob = _class(message, part, use, namespace, encstyle)
  639. self.addExtension(ob)
  640. continue
  641. elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'urlReplacement':
  642. ob = HttpUrlReplacementBinding()
  643. self.addExtension(ob)
  644. continue
  645. elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'urlEncoded':
  646. ob = HttpUrlEncodedBinding()
  647. self.addExtension(ob)
  648. continue
  649. elif ns in DOM.NS_MIME_BINDING_ALL and name == 'multipartRelated':
  650. ob = MimeMultipartRelatedBinding()
  651. self.addExtension(ob)
  652. ob.load_ex(GetExtensions(e))
  653. continue
  654. elif ns in DOM.NS_MIME_BINDING_ALL and name == 'content':
  655. part = DOM.getAttr(e, 'part', default=None)
  656. type = DOM.getAttr(e, 'type', default=None)
  657. ob = MimeContentBinding(part, type)
  658. self.addExtension(ob)
  659. continue
  660. elif ns in DOM.NS_MIME_BINDING_ALL and name == 'mimeXml':
  661. part = DOM.getAttr(e, 'part', default=None)
  662. ob = MimeXmlBinding(part)
  663. self.addExtension(ob)
  664. continue
  665. else:
  666. self.addExtension(e)
  667. class Service(Element):
  668. def __init__(self, name, documentation=''):
  669. Element.__init__(self, name, documentation)
  670. self.ports = Collection(self)
  671. def getWSDL(self):
  672. return self.parent().parent()
  673. def addPort(self, name, binding, documentation=''):
  674. item = Port(name, binding, documentation)
  675. self.ports[name] = item
  676. return item
  677. def load(self, elements):
  678. for element in elements:
  679. name = DOM.getAttr(element, 'name', default=None)
  680. docs = GetDocumentation(element)
  681. binding = DOM.getAttr(element, 'binding', default=None)
  682. if name is None or binding is None:
  683. raise WSDLError(
  684. 'Invalid port element.'
  685. )
  686. binding = ParseQName(binding, element)
  687. port = self.addPort(name, binding, docs)
  688. port.load_ex(GetExtensions(element))
  689. def load_ex(self, elements):
  690. for e in elements:
  691. self.addExtension(e)
  692. class Port(Element):
  693. def __init__(self, name, binding, documentation=''):
  694. Element.__init__(self, name, documentation)
  695. self.binding = binding
  696. def getService(self):
  697. """Return the Service object associated with this port."""
  698. return self.parent().parent()
  699. def getBinding(self):
  700. """Return the Binding object that is referenced by this port."""
  701. wsdl = self.getService().getWSDL()
  702. return wsdl.bindings[self.binding]
  703. def getPortType(self):
  704. """Return the PortType object that is referenced by this port."""
  705. wsdl = self.getService().getWSDL()
  706. binding = wsdl.bindings[self.binding]
  707. return wsdl.portTypes[binding.type]
  708. def getAddressBinding(self):
  709. """A convenience method to obtain the extension element used
  710. as the address binding for the port, or None if undefined."""
  711. for item in self.extensions:
  712. if isinstance(item, SoapAddressBinding) or \
  713. isinstance(item, HttpAddressBinding):
  714. return item
  715. raise WSDLError(
  716. 'No address binding found in port.'
  717. )
  718. def load_ex(self, elements):
  719. for e in elements:
  720. ns, name = e.namespaceURI, e.localName
  721. if ns in DOM.NS_SOAP_BINDING_ALL and name == 'address':
  722. location = DOM.getAttr(e, 'location', default=None)
  723. ob = SoapAddressBinding(location)
  724. self.addExtension(ob)
  725. continue
  726. elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'address':
  727. location = DOM.getAttr(e, 'location', default=None)
  728. ob = HttpAddressBinding(location)
  729. self.addExtension(ob)
  730. continue
  731. else:
  732. self.addExtension(e)
  733. class SoapBinding:
  734. def __init__(self, transport, style='rpc'):
  735. self.transport = transport
  736. self.style = style
  737. class SoapAddressBinding:
  738. def __init__(self, location):
  739. self.location = location
  740. class SoapOperationBinding:
  741. def __init__(self, soapAction=None, style=None):
  742. self.soapAction = soapAction
  743. self.style = style
  744. class SoapBodyBinding:
  745. def __init__(self, use, namespace=None, encodingStyle=None, parts=None):
  746. if not use in ('literal', 'encoded'):
  747. raise WSDLError(
  748. 'Invalid use attribute value: %s' % use
  749. )
  750. self.encodingStyle = encodingStyle
  751. self.namespace = namespace
  752. if type(parts) in (type(''), type(u'')):
  753. parts = parts.split()
  754. self.parts = parts
  755. self.use = use
  756. class SoapFaultBinding:
  757. def __init__(self, name, use, namespace=None, encodingStyle=None):
  758. if not use in ('literal', 'encoded'):
  759. raise WSDLError(
  760. 'Invalid use attribute value: %s' % use
  761. )
  762. self.encodingStyle = encodingStyle
  763. self.namespace = namespace
  764. self.name = name
  765. self.use = use
  766. class SoapHeaderBinding:
  767. def __init__(self, message, part, use, namespace=None, encodingStyle=None):
  768. if not use in ('literal', 'encoded'):
  769. raise WSDLError(
  770. 'Invalid use attribute value: %s' % use
  771. )
  772. self.encodingStyle = encodingStyle
  773. self.namespace = namespace
  774. self.message = message
  775. self.part = part
  776. self.use = use
  777. tagname = 'header'
  778. class SoapHeaderFaultBinding(SoapHeaderBinding):
  779. tagname = 'headerfault'
  780. class HttpBinding:
  781. def __init__(self, verb):
  782. self.verb = verb
  783. class HttpAddressBinding:
  784. def __init__(self, location):
  785. self.location = location
  786. class HttpOperationBinding:
  787. def __init__(self, location):
  788. self.location = location
  789. class HttpUrlReplacementBinding:
  790. pass
  791. class HttpUrlEncodedBinding:
  792. pass
  793. class MimeContentBinding:
  794. def __init__(self, part=None, type=None):
  795. self.part = part
  796. self.type = type
  797. class MimeXmlBinding:
  798. def __init__(self, part=None):
  799. self.part = part
  800. class MimeMultipartRelatedBinding:
  801. def __init__(self):
  802. self.parts = []
  803. def load_ex(self, elements):
  804. for e in elements:
  805. ns, name = e.namespaceURI, e.localName
  806. if ns in DOM.NS_MIME_BINDING_ALL and name == 'part':
  807. self.parts.append(MimePartBinding())
  808. continue
  809. class MimePartBinding:
  810. def __init__(self):
  811. self.items = []
  812. def load_ex(self, elements):
  813. for e in elements:
  814. ns, name = e.namespaceURI, e.localName
  815. if ns in DOM.NS_MIME_BINDING_ALL and name == 'content':
  816. part = DOM.getAttr(e, 'part', default=None)
  817. type = DOM.getAttr(e, 'type', default=None)
  818. ob = MimeContentBinding(part, type)
  819. self.items.append(ob)
  820. continue
  821. elif ns in DOM.NS_MIME_BINDING_ALL and name == 'mimeXml':
  822. part = DOM.getAttr(e, 'part', default=None)
  823. ob = MimeXmlBinding(part)
  824. self.items.append(ob)
  825. continue
  826. elif ns in DOM.NS_SOAP_BINDING_ALL and name == 'body':
  827. encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
  828. namespace = DOM.getAttr(e, 'namespace', default=None)
  829. parts = DOM.getAttr(e, 'parts', default=None)
  830. use = DOM.getAttr(e, 'use', default=None)
  831. if use is None:
  832. raise WSDLError(
  833. 'Invalid soap:body binding element.'
  834. )
  835. ob = SoapBodyBinding(use, namespace, encstyle, parts)
  836. self.items.append(ob)
  837. continue
  838. class WSDLError(Exception):
  839. pass
  840. def DeclareNSPrefix(writer, prefix, nsuri):
  841. if writer.hasNSPrefix(nsuri):
  842. return
  843. writer.declareNSPrefix(prefix, nsuri)
  844. def ParseTypeRef(value, element):
  845. parts = value.split(':', 1)
  846. if len(parts) == 1:
  847. return (DOM.findTargetNS(element), value)
  848. nsuri = DOM.findNamespaceURI(parts[0], element)
  849. return (nsuri, parts[1])
  850. def ParseQName(value, element):
  851. nameref = value.split(':', 1)
  852. if len(nameref) == 2:
  853. nsuri = DOM.findNamespaceURI(nameref[0], element)
  854. name = nameref[-1]
  855. else:
  856. nsuri = DOM.findTargetNS(element)
  857. name = nameref[-1]
  858. return nsuri, name
  859. def GetDocumentation(element):
  860. docnode = DOM.getElement(element, 'documentation', None, None)
  861. if docnode is not None:
  862. return DOM.getElementText(docnode)
  863. return ''
  864. def GetExtensions(element):
  865. return [ item for item in DOM.getElements(element, None, None)
  866. if item.namespaceURI != DOM.NS_WSDL ]
  867. def GetWSAActionFault(operation, name):
  868. """Find wsa:Action attribute, and return value or WSA.FAULT
  869. for the default.
  870. """
  871. attr = operation.faults[name].action
  872. if attr is not None:
  873. return attr
  874. return WSA.FAULT
  875. def GetWSAActionInput(operation):
  876. """Find wsa:Action attribute, and return value or the default."""
  877. attr = operation.input.action
  878. if attr is not None:
  879. return attr
  880. portType = operation.getPortType()
  881. targetNamespace = portType.getWSDL().targetNamespace
  882. ptName = portType.name
  883. msgName = operation.input.name
  884. if not msgName:
  885. msgName = operation.name + 'Request'
  886. if targetNamespace.endswith('/'):
  887. return '%s%s/%s' %(targetNamespace, ptName, msgName)
  888. return '%s/%s/%s' %(targetNamespace, ptName, msgName)
  889. def GetWSAActionOutput(operation):
  890. """Find wsa:Action attribute, and return value or the default."""
  891. attr = operation.output.action
  892. if attr is not None:
  893. return attr
  894. targetNamespace = operation.getPortType().getWSDL().targetNamespace
  895. ptName = operation.getPortType().name
  896. msgName = operation.output.name
  897. if not msgName:
  898. msgName = operation.name + 'Response'
  899. if targetNamespace.endswith('/'):
  900. return '%s%s/%s' %(targetNamespace, ptName, msgName)
  901. return '%s/%s/%s' %(targetNamespace, ptName, msgName)
  902. def FindExtensions(object, kind, t_type=type(())):
  903. if isinstance(kind, t_type):
  904. result = []
  905. namespaceURI, name = kind
  906. return [ item for item in object.extensions
  907. if hasattr(item, 'nodeType') \
  908. and DOM.nsUriMatch(namespaceURI, item.namespaceURI) \
  909. and item.name == name ]
  910. return [ item for item in object.extensions if isinstance(item, kind) ]
  911. def FindExtension(object, kind, t_type=type(())):
  912. if isinstance(kind, t_type):
  913. namespaceURI, name = kind
  914. for item in object.extensions:
  915. if hasattr(item, 'nodeType') \
  916. and DOM.nsUriMatch(namespaceURI, item.namespaceURI) \
  917. and item.name == name:
  918. return item
  919. else:
  920. for item in object.extensions:
  921. if isinstance(item, kind):
  922. return item
  923. return None
  924. class SOAPCallInfo:
  925. """SOAPCallInfo captures the important binding information about a
  926. SOAP operation, in a structure that is easier to work with than
  927. raw WSDL structures."""
  928. def __init__(self, methodName):
  929. self.methodName = methodName
  930. self.inheaders = []
  931. self.outheaders = []
  932. self.inparams = []
  933. self.outparams = []
  934. self.retval = None
  935. encodingStyle = DOM.NS_SOAP_ENC
  936. documentation = ''
  937. soapAction = None
  938. transport = None
  939. namespace = None
  940. location = None
  941. use = 'encoded'
  942. style = 'rpc'
  943. def addInParameter(self, name, type, namespace=None, element_type=0):
  944. """Add an input parameter description to the call info."""
  945. parameter = ParameterInfo(name, type, namespace, element_type)
  946. self.inparams.append(parameter)
  947. return parameter
  948. def addOutParameter(self, name, type, namespace=None, element_type=0):
  949. """Add an output parameter description to the call info."""
  950. parameter = ParameterInfo(name, type, namespace, element_type)
  951. self.outparams.append(parameter)
  952. return parameter
  953. def setReturnParameter(self, name, type, namespace=None, element_type=0):
  954. """Set the return parameter description for the call info."""
  955. parameter = ParameterInfo(name, type, namespace, element_type)
  956. self.retval = parameter
  957. return parameter
  958. def addInHeaderInfo(self, name, type, namespace, element_type=0,
  959. mustUnderstand=0):
  960. """Add an input SOAP header description to the call info."""
  961. headerinfo = HeaderInfo(name, type, namespace, element_type)
  962. if mustUnderstand:
  963. headerinfo.mustUnderstand = 1
  964. self.inheaders.append(headerinfo)
  965. return headerinfo
  966. def addOutHeaderInfo(self, name, type, namespace, element_type=0,
  967. mustUnderstand=0):
  968. """Add an output SOAP header description to the call info."""
  969. headerinfo = HeaderInfo(name, type, namespace, element_type)
  970. if mustUnderstand:
  971. headerinfo.mustUnderstand = 1
  972. self.outheaders.append(headerinfo)
  973. return headerinfo
  974. def getInParameters(self):
  975. """Return a sequence of the in parameters of the method."""
  976. return self.inparams
  977. def getOutParameters(self):
  978. """Return a sequence of the out parameters of the method."""
  979. return self.outparams
  980. def getReturnParameter(self):
  981. """Return param info about the return value of the method."""
  982. return self.retval
  983. def getInHeaders(self):
  984. """Return a sequence of the in headers of the method."""
  985. return self.inheaders
  986. def getOutHeaders(self):
  987. """Return a sequence of the out headers of the method."""
  988. return self.outheaders
  989. class ParameterInfo:
  990. """A ParameterInfo object captures parameter binding information."""
  991. def __init__(self, name, type, namespace=None, element_type=0):
  992. if element_type:
  993. self.element_type = 1
  994. if namespace is not None:
  995. self.namespace = namespace
  996. self.name = name
  997. self.type = type
  998. element_type = 0
  999. namespace = None
  1000. default = None
  1001. class HeaderInfo(ParameterInfo):
  1002. """A HeaderInfo object captures SOAP header binding information."""
  1003. def __init__(self, name, type, namespace, element_type=None):
  1004. ParameterInfo.__init__(self, name, type, namespace, element_type)
  1005. mustUnderstand = 0
  1006. actor = None
  1007. def callInfoFromWSDL(port, name):
  1008. """Return a SOAPCallInfo given a WSDL port and operation name."""
  1009. wsdl = port.getService().getWSDL()
  1010. binding = port.getBinding()
  1011. portType = binding.getPortType()
  1012. operation = portType.operations[name]
  1013. opbinding = binding.operations[name]
  1014. messages = wsdl.messages
  1015. callinfo = SOAPCallInfo(name)
  1016. addrbinding = port.getAddressBinding()
  1017. if not isinstance(addrbinding, SoapAddressBinding):
  1018. raise ValueError, 'Unsupported binding type.'
  1019. callinfo.location = addrbinding.location
  1020. soapbinding = binding.findBinding(SoapBinding)
  1021. if soapbinding is None:
  1022. raise ValueError, 'Missing soap:binding element.'
  1023. callinfo.transport = soapbinding.transport
  1024. callinfo.style = soapbinding.style or 'document'
  1025. soap_op_binding = opbinding.findBinding(SoapOperationBinding)
  1026. if soap_op_binding is not None:
  1027. callinfo.soapAction = soap_op_binding.soapAction
  1028. callinfo.style = soap_op_binding.style or callinfo.style
  1029. parameterOrder = operation.parameterOrder
  1030. if operation.input is not None:
  1031. message = messages[operation.input.message]
  1032. msgrole = opbinding.input
  1033. mime = msgrole.findBinding(MimeMultipartRelatedBinding)
  1034. if mime is not None:
  1035. raise ValueError, 'Mime bindings are not supported.'
  1036. else:
  1037. for item in msgrole.findBindings(SoapHeaderBinding):
  1038. part = messages[item.message].parts[item.part]
  1039. header = callinfo.addInHeaderInfo(
  1040. part.name,
  1041. part.element or part.type,
  1042. item.namespace,
  1043. element_type = part.element and 1 or 0
  1044. )
  1045. header.encodingStyle = item.encodingStyle
  1046. body = msgrole.findBinding(SoapBodyBinding)
  1047. if body is None:
  1048. raise ValueError, 'Missing soap:body binding.'
  1049. callinfo.encodingStyle = body.encodingStyle
  1050. callinfo.namespace = body.namespace
  1051. callinfo.use = body.use
  1052. if body.parts is not None:
  1053. parts = []
  1054. for name in body.parts:
  1055. parts.append(message.parts[name])
  1056. else:
  1057. parts = message.parts.values()
  1058. for part in parts:
  1059. callinfo.addInParameter(
  1060. part.name,
  1061. part.element or part.type,
  1062. element_type = part.element and 1 or 0
  1063. )
  1064. if operation.output is not None:
  1065. try:
  1066. message = messages[operation.output.message]
  1067. except KeyError:
  1068. if self.strict:
  1069. raise RuntimeError(
  1070. "Recieved message not defined in the WSDL schema: %s" %
  1071. operation.output.message)
  1072. else:
  1073. message = wsdl.addMessage(operation.output.message)
  1074. print "Warning:", \
  1075. "Recieved message not defined in the WSDL schema.", \
  1076. "Adding it."
  1077. print "Message:", operation.output.message
  1078. msgrole = opbinding.output
  1079. mime = msgrole.findBinding(MimeMultipartRelatedBinding)
  1080. if mime is not None:
  1081. raise ValueError, 'Mime bindings are not supported.'
  1082. else:
  1083. for item in msgrole.findBindings(SoapHeaderBinding):
  1084. part = messages[item.message].parts[item.part]
  1085. header = callinfo.addOutHeaderInfo(
  1086. part.name,
  1087. part.element or part.type,
  1088. item.namespace,
  1089. element_type = part.element and 1 or 0
  1090. )
  1091. header.encodingStyle = item.encodingStyle
  1092. body = msgrole.findBinding(SoapBodyBinding)
  1093. if body is None:
  1094. raise ValueError, 'Missing soap:body binding.'
  1095. callinfo.encodingStyle = body.encodingStyle
  1096. callinfo.namespace = body.namespace
  1097. callinfo.use = body.use
  1098. if body.parts is not None:
  1099. parts = []
  1100. for name in body.parts:
  1101. parts.append(message.parts[name])
  1102. else:
  1103. parts = message.parts.values()
  1104. if parts:
  1105. # XXX no idea what this is for, but it breaks everything. jrb
  1106. #callinfo.setReturnParameter(
  1107. # parts[0].name,
  1108. # parts[0].element or parts[0].type,
  1109. # element_type = parts[0].element and 1 or 0
  1110. # )
  1111. #for part in parts[1:]:
  1112. for part in parts:
  1113. callinfo.addOutParameter(
  1114. part.name,
  1115. part.element or part.type,
  1116. element_type = part.element and 1 or 0
  1117. )
  1118. return callinfo