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.
 
 
 

1347 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 getTargetNamespace(self):
  380. return self.targetNamespace or self.getWSDL().targetNamespace
  381. def getResourceProperties(self):
  382. return self.resourceProperties
  383. def addOperation(self, name, documentation='', parameterOrder=None):
  384. item = Operation(name, documentation, parameterOrder)
  385. self.operations[name] = item
  386. return item
  387. def load(self, element):
  388. self.name = DOM.getAttr(element, 'name')
  389. self.documentation = GetDocumentation(element)
  390. self.targetNamespace = DOM.getAttr(element, 'targetNamespace')
  391. if DOM.hasAttr(element, 'ResourceProperties', WSR.PROPERTIES):
  392. rpref = DOM.getAttr(element, 'ResourceProperties', WSR.PROPERTIES)
  393. self.resourceProperties = ParseQName(rpref, element)
  394. NS_WSDL = DOM.GetWSDLUri(self.getWSDL().version)
  395. elements = DOM.getElements(element, 'operation', NS_WSDL)
  396. for element in elements:
  397. name = DOM.getAttr(element, 'name')
  398. docs = GetDocumentation(element)
  399. param_order = DOM.getAttr(element, 'parameterOrder', default=None)
  400. if param_order is not None:
  401. param_order = param_order.split(' ')
  402. operation = self.addOperation(name, docs, param_order)
  403. item = DOM.getElement(element, 'input', None, None)
  404. if item is not None:
  405. name = DOM.getAttr(item, 'name')
  406. docs = GetDocumentation(item)
  407. msgref = DOM.getAttr(item, 'message')
  408. message = ParseQName(msgref, item)
  409. action = DOM.getAttr(item, 'Action', WSA.ADDRESS2004, None)
  410. if not action:
  411. action = DOM.getAttr(item, 'Action', WSA.ADDRESS, None)
  412. operation.setInput(message, name, docs, action)
  413. item = DOM.getElement(element, 'output', None, None)
  414. if item is not None:
  415. name = DOM.getAttr(item, 'name')
  416. docs = GetDocumentation(item)
  417. msgref = DOM.getAttr(item, 'message')
  418. message = ParseQName(msgref, item)
  419. action = DOM.getAttr(item, 'Action', WSA.ADDRESS2004, None)
  420. if not action:
  421. action = DOM.getAttr(item, 'Action', WSA.ADDRESS, None)
  422. operation.setOutput(message, name, docs, action)
  423. for item in DOM.getElements(element, 'fault', None):
  424. name = DOM.getAttr(item, 'name')
  425. docs = GetDocumentation(item)
  426. msgref = DOM.getAttr(item, 'message')
  427. message = ParseQName(msgref, item)
  428. action = DOM.getAttr(item, 'Action', WSA.ADDRESS2004, None)
  429. if not action:
  430. action = DOM.getAttr(item, 'Action', WSA.ADDRESS, None)
  431. operation.addFault(message, name, docs, action)
  432. class Operation(Element):
  433. def __init__(self, name, documentation='', parameterOrder=None):
  434. Element.__init__(self, name, documentation)
  435. self.parameterOrder = parameterOrder
  436. self.faults = Collection(self)
  437. self.input = None
  438. self.output = None
  439. def getPortType(self):
  440. return self.parent().parent()
  441. def getInputAction(self):
  442. """wsa:Action attribute"""
  443. return GetWSAActionInput(self)
  444. def getInputMessage(self):
  445. if self.input is None:
  446. return None
  447. wsdl = self.getPortType().getWSDL()
  448. return wsdl.messages[self.input.message]
  449. def getOutputAction(self):
  450. """wsa:Action attribute"""
  451. return GetWSAActionOutput(self)
  452. def getOutputMessage(self):
  453. if self.output is None:
  454. return None
  455. wsdl = self.getPortType().getWSDL()
  456. return wsdl.messages[self.output.message]
  457. def getFaultAction(self, name):
  458. """wsa:Action attribute"""
  459. return GetWSAActionFault(self, name)
  460. def getFaultMessage(self, name):
  461. wsdl = self.getPortType().getWSDL()
  462. return wsdl.messages[self.faults[name].message]
  463. def addFault(self, message, name, documentation='', action=None):
  464. if self.faults.has_key(name):
  465. raise WSDLError(
  466. 'Duplicate fault element: %s' % name
  467. )
  468. item = MessageRole('fault', message, name, documentation, action)
  469. self.faults[name] = item
  470. return item
  471. def setInput(self, message, name='', documentation='', action=None):
  472. self.input = MessageRole('input', message, name, documentation, action)
  473. return self.input
  474. def setOutput(self, message, name='', documentation='', action=None):
  475. self.output = MessageRole('output', message, name, documentation, action)
  476. return self.output
  477. class MessageRole(Element):
  478. def __init__(self, type, message, name='', documentation='', action=None):
  479. Element.__init__(self, name, documentation)
  480. self.message = message
  481. self.type = type
  482. self.action = action
  483. class Binding(Element):
  484. def __init__(self, name, type, documentation=''):
  485. Element.__init__(self, name, documentation)
  486. self.operations = Collection(self)
  487. self.type = type
  488. def getWSDL(self):
  489. """Return the WSDL object that contains this binding."""
  490. return self.parent().parent()
  491. def getPortType(self):
  492. """Return the PortType object associated with this binding."""
  493. return self.getWSDL().portTypes[self.type]
  494. def findBinding(self, kind):
  495. for item in self.extensions:
  496. if isinstance(item, kind):
  497. return item
  498. return None
  499. def findBindings(self, kind):
  500. return [ item for item in self.extensions if isinstance(item, kind) ]
  501. def addOperationBinding(self, name, documentation=''):
  502. item = OperationBinding(name, documentation)
  503. self.operations[name] = item
  504. return item
  505. def load(self, elements):
  506. for element in elements:
  507. name = DOM.getAttr(element, 'name')
  508. docs = GetDocumentation(element)
  509. opbinding = self.addOperationBinding(name, docs)
  510. opbinding.load_ex(GetExtensions(element))
  511. item = DOM.getElement(element, 'input', None, None)
  512. if item is not None:
  513. mbinding = MessageRoleBinding('input')
  514. mbinding.documentation = GetDocumentation(item)
  515. opbinding.input = mbinding
  516. mbinding.load_ex(GetExtensions(item))
  517. item = DOM.getElement(element, 'output', None, None)
  518. if item is not None:
  519. mbinding = MessageRoleBinding('output')
  520. mbinding.documentation = GetDocumentation(item)
  521. opbinding.output = mbinding
  522. mbinding.load_ex(GetExtensions(item))
  523. for item in DOM.getElements(element, 'fault', None):
  524. name = DOM.getAttr(item, 'name')
  525. mbinding = MessageRoleBinding('fault', name)
  526. mbinding.documentation = GetDocumentation(item)
  527. opbinding.faults[name] = mbinding
  528. mbinding.load_ex(GetExtensions(item))
  529. def load_ex(self, elements):
  530. for e in elements:
  531. ns, name = e.namespaceURI, e.localName
  532. if ns in DOM.NS_SOAP_BINDING_ALL and name == 'binding':
  533. transport = DOM.getAttr(e, 'transport', default=None)
  534. style = DOM.getAttr(e, 'style', default='document')
  535. ob = SoapBinding(transport, style)
  536. self.addExtension(ob)
  537. continue
  538. elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'binding':
  539. verb = DOM.getAttr(e, 'verb')
  540. ob = HttpBinding(verb)
  541. self.addExtension(ob)
  542. continue
  543. else:
  544. self.addExtension(e)
  545. class OperationBinding(Element):
  546. def __init__(self, name, documentation=''):
  547. Element.__init__(self, name, documentation)
  548. self.input = None
  549. self.output = None
  550. self.faults = Collection(self)
  551. def getBinding(self):
  552. """Return the parent Binding object of the operation binding."""
  553. return self.parent().parent()
  554. def getOperation(self):
  555. """Return the abstract Operation associated with this binding."""
  556. return self.getBinding().getPortType().operations[self.name]
  557. def findBinding(self, kind):
  558. for item in self.extensions:
  559. if isinstance(item, kind):
  560. return item
  561. return None
  562. def findBindings(self, kind):
  563. return [ item for item in self.extensions if isinstance(item, kind) ]
  564. def addInputBinding(self, binding):
  565. if self.input is None:
  566. self.input = MessageRoleBinding('input')
  567. self.input.addExtension(binding)
  568. return binding
  569. def addOutputBinding(self, binding):
  570. if self.output is None:
  571. self.output = MessageRoleBinding('output')
  572. self.output.addExtension(binding)
  573. return binding
  574. def addFaultBinding(self, name, binding):
  575. fault = self.get(name, None)
  576. if fault is None:
  577. fault = MessageRoleBinding('fault', name)
  578. fault.addExtension(binding)
  579. return binding
  580. def load_ex(self, elements):
  581. for e in elements:
  582. ns, name = e.namespaceURI, e.localName
  583. if ns in DOM.NS_SOAP_BINDING_ALL and name == 'operation':
  584. soapaction = DOM.getAttr(e, 'soapAction', default=None)
  585. style = DOM.getAttr(e, 'style', default=None)
  586. ob = SoapOperationBinding(soapaction, style)
  587. self.addExtension(ob)
  588. continue
  589. elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'operation':
  590. location = DOM.getAttr(e, 'location')
  591. ob = HttpOperationBinding(location)
  592. self.addExtension(ob)
  593. continue
  594. else:
  595. self.addExtension(e)
  596. class MessageRoleBinding(Element):
  597. def __init__(self, type, name='', documentation=''):
  598. Element.__init__(self, name, documentation)
  599. self.type = type
  600. def findBinding(self, kind):
  601. for item in self.extensions:
  602. if isinstance(item, kind):
  603. return item
  604. return None
  605. def findBindings(self, kind):
  606. return [ item for item in self.extensions if isinstance(item, kind) ]
  607. def load_ex(self, elements):
  608. for e in elements:
  609. ns, name = e.namespaceURI, e.localName
  610. if ns in DOM.NS_SOAP_BINDING_ALL and name == 'body':
  611. encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
  612. namespace = DOM.getAttr(e, 'namespace', default=None)
  613. parts = DOM.getAttr(e, 'parts', default=None)
  614. use = DOM.getAttr(e, 'use', default=None)
  615. if use is None:
  616. raise WSDLError(
  617. 'Invalid soap:body binding element.'
  618. )
  619. ob = SoapBodyBinding(use, namespace, encstyle, parts)
  620. self.addExtension(ob)
  621. continue
  622. elif ns in DOM.NS_SOAP_BINDING_ALL and name == 'fault':
  623. encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
  624. namespace = DOM.getAttr(e, 'namespace', default=None)
  625. name = DOM.getAttr(e, 'name', default=None)
  626. use = DOM.getAttr(e, 'use', default=None)
  627. if use is None or name is None:
  628. raise WSDLError(
  629. 'Invalid soap:fault binding element.'
  630. )
  631. ob = SoapFaultBinding(name, use, namespace, encstyle)
  632. self.addExtension(ob)
  633. continue
  634. elif ns in DOM.NS_SOAP_BINDING_ALL and name in (
  635. 'header', 'headerfault'
  636. ):
  637. encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
  638. namespace = DOM.getAttr(e, 'namespace', default=None)
  639. message = DOM.getAttr(e, 'message')
  640. part = DOM.getAttr(e, 'part')
  641. use = DOM.getAttr(e, 'use')
  642. if name == 'header':
  643. _class = SoapHeaderBinding
  644. else:
  645. _class = SoapHeaderFaultBinding
  646. message = ParseQName(message, e)
  647. ob = _class(message, part, use, namespace, encstyle)
  648. self.addExtension(ob)
  649. continue
  650. elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'urlReplacement':
  651. ob = HttpUrlReplacementBinding()
  652. self.addExtension(ob)
  653. continue
  654. elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'urlEncoded':
  655. ob = HttpUrlEncodedBinding()
  656. self.addExtension(ob)
  657. continue
  658. elif ns in DOM.NS_MIME_BINDING_ALL and name == 'multipartRelated':
  659. ob = MimeMultipartRelatedBinding()
  660. self.addExtension(ob)
  661. ob.load_ex(GetExtensions(e))
  662. continue
  663. elif ns in DOM.NS_MIME_BINDING_ALL and name == 'content':
  664. part = DOM.getAttr(e, 'part', default=None)
  665. type = DOM.getAttr(e, 'type', default=None)
  666. ob = MimeContentBinding(part, type)
  667. self.addExtension(ob)
  668. continue
  669. elif ns in DOM.NS_MIME_BINDING_ALL and name == 'mimeXml':
  670. part = DOM.getAttr(e, 'part', default=None)
  671. ob = MimeXmlBinding(part)
  672. self.addExtension(ob)
  673. continue
  674. else:
  675. self.addExtension(e)
  676. class Service(Element):
  677. def __init__(self, name, documentation=''):
  678. Element.__init__(self, name, documentation)
  679. self.ports = Collection(self)
  680. def getWSDL(self):
  681. return self.parent().parent()
  682. def addPort(self, name, binding, documentation=''):
  683. item = Port(name, binding, documentation)
  684. self.ports[name] = item
  685. return item
  686. def load(self, elements):
  687. for element in elements:
  688. name = DOM.getAttr(element, 'name', default=None)
  689. docs = GetDocumentation(element)
  690. binding = DOM.getAttr(element, 'binding', default=None)
  691. if name is None or binding is None:
  692. raise WSDLError(
  693. 'Invalid port element.'
  694. )
  695. binding = ParseQName(binding, element)
  696. port = self.addPort(name, binding, docs)
  697. port.load_ex(GetExtensions(element))
  698. def load_ex(self, elements):
  699. for e in elements:
  700. self.addExtension(e)
  701. class Port(Element):
  702. def __init__(self, name, binding, documentation=''):
  703. Element.__init__(self, name, documentation)
  704. self.binding = binding
  705. def getService(self):
  706. """Return the Service object associated with this port."""
  707. return self.parent().parent()
  708. def getBinding(self):
  709. """Return the Binding object that is referenced by this port."""
  710. wsdl = self.getService().getWSDL()
  711. return wsdl.bindings[self.binding]
  712. def getPortType(self):
  713. """Return the PortType object that is referenced by this port."""
  714. wsdl = self.getService().getWSDL()
  715. binding = wsdl.bindings[self.binding]
  716. return wsdl.portTypes[binding.type]
  717. def getAddressBinding(self):
  718. """A convenience method to obtain the extension element used
  719. as the address binding for the port, or None if undefined."""
  720. for item in self.extensions:
  721. if isinstance(item, SoapAddressBinding) or \
  722. isinstance(item, HttpAddressBinding):
  723. return item
  724. raise WSDLError(
  725. 'No address binding found in port.'
  726. )
  727. def load_ex(self, elements):
  728. for e in elements:
  729. ns, name = e.namespaceURI, e.localName
  730. if ns in DOM.NS_SOAP_BINDING_ALL and name == 'address':
  731. location = DOM.getAttr(e, 'location', default=None)
  732. ob = SoapAddressBinding(location)
  733. self.addExtension(ob)
  734. continue
  735. elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'address':
  736. location = DOM.getAttr(e, 'location', default=None)
  737. ob = HttpAddressBinding(location)
  738. self.addExtension(ob)
  739. continue
  740. else:
  741. self.addExtension(e)
  742. class SoapBinding:
  743. def __init__(self, transport, style='rpc'):
  744. self.transport = transport
  745. self.style = style
  746. class SoapAddressBinding:
  747. def __init__(self, location):
  748. self.location = location
  749. class SoapOperationBinding:
  750. def __init__(self, soapAction=None, style=None):
  751. self.soapAction = soapAction
  752. self.style = style
  753. class SoapBodyBinding:
  754. def __init__(self, use, namespace=None, encodingStyle=None, parts=None):
  755. if not use in ('literal', 'encoded'):
  756. raise WSDLError(
  757. 'Invalid use attribute value: %s' % use
  758. )
  759. self.encodingStyle = encodingStyle
  760. self.namespace = namespace
  761. if type(parts) in (type(''), type(u'')):
  762. parts = parts.split()
  763. self.parts = parts
  764. self.use = use
  765. class SoapFaultBinding:
  766. def __init__(self, name, use, namespace=None, encodingStyle=None):
  767. if not use in ('literal', 'encoded'):
  768. raise WSDLError(
  769. 'Invalid use attribute value: %s' % use
  770. )
  771. self.encodingStyle = encodingStyle
  772. self.namespace = namespace
  773. self.name = name
  774. self.use = use
  775. class SoapHeaderBinding:
  776. def __init__(self, message, part, use, namespace=None, encodingStyle=None):
  777. if not use in ('literal', 'encoded'):
  778. raise WSDLError(
  779. 'Invalid use attribute value: %s' % use
  780. )
  781. self.encodingStyle = encodingStyle
  782. self.namespace = namespace
  783. self.message = message
  784. self.part = part
  785. self.use = use
  786. tagname = 'header'
  787. class SoapHeaderFaultBinding(SoapHeaderBinding):
  788. tagname = 'headerfault'
  789. class HttpBinding:
  790. def __init__(self, verb):
  791. self.verb = verb
  792. class HttpAddressBinding:
  793. def __init__(self, location):
  794. self.location = location
  795. class HttpOperationBinding:
  796. def __init__(self, location):
  797. self.location = location
  798. class HttpUrlReplacementBinding:
  799. pass
  800. class HttpUrlEncodedBinding:
  801. pass
  802. class MimeContentBinding:
  803. def __init__(self, part=None, type=None):
  804. self.part = part
  805. self.type = type
  806. class MimeXmlBinding:
  807. def __init__(self, part=None):
  808. self.part = part
  809. class MimeMultipartRelatedBinding:
  810. def __init__(self):
  811. self.parts = []
  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 == 'part':
  816. self.parts.append(MimePartBinding())
  817. continue
  818. class MimePartBinding:
  819. def __init__(self):
  820. self.items = []
  821. def load_ex(self, elements):
  822. for e in elements:
  823. ns, name = e.namespaceURI, e.localName
  824. if ns in DOM.NS_MIME_BINDING_ALL and name == 'content':
  825. part = DOM.getAttr(e, 'part', default=None)
  826. type = DOM.getAttr(e, 'type', default=None)
  827. ob = MimeContentBinding(part, type)
  828. self.items.append(ob)
  829. continue
  830. elif ns in DOM.NS_MIME_BINDING_ALL and name == 'mimeXml':
  831. part = DOM.getAttr(e, 'part', default=None)
  832. ob = MimeXmlBinding(part)
  833. self.items.append(ob)
  834. continue
  835. elif ns in DOM.NS_SOAP_BINDING_ALL and name == 'body':
  836. encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
  837. namespace = DOM.getAttr(e, 'namespace', default=None)
  838. parts = DOM.getAttr(e, 'parts', default=None)
  839. use = DOM.getAttr(e, 'use', default=None)
  840. if use is None:
  841. raise WSDLError(
  842. 'Invalid soap:body binding element.'
  843. )
  844. ob = SoapBodyBinding(use, namespace, encstyle, parts)
  845. self.items.append(ob)
  846. continue
  847. class WSDLError(Exception):
  848. pass
  849. def DeclareNSPrefix(writer, prefix, nsuri):
  850. if writer.hasNSPrefix(nsuri):
  851. return
  852. writer.declareNSPrefix(prefix, nsuri)
  853. def ParseTypeRef(value, element):
  854. parts = value.split(':', 1)
  855. if len(parts) == 1:
  856. return (DOM.findTargetNS(element), value)
  857. nsuri = DOM.findNamespaceURI(parts[0], element)
  858. return (nsuri, parts[1])
  859. def ParseQName(value, element):
  860. nameref = value.split(':', 1)
  861. if len(nameref) == 2:
  862. nsuri = DOM.findNamespaceURI(nameref[0], element)
  863. name = nameref[-1]
  864. else:
  865. nsuri = DOM.findTargetNS(element)
  866. name = nameref[-1]
  867. return nsuri, name
  868. def GetDocumentation(element):
  869. docnode = DOM.getElement(element, 'documentation', None, None)
  870. if docnode is not None:
  871. return DOM.getElementText(docnode)
  872. return ''
  873. def GetExtensions(element):
  874. return [ item for item in DOM.getElements(element, None, None)
  875. if item.namespaceURI != DOM.NS_WSDL ]
  876. def GetWSAActionFault(operation, name):
  877. """Find wsa:Action attribute, and return value or WSA.FAULT
  878. for the default.
  879. """
  880. attr = operation.faults[name].action
  881. if attr is not None:
  882. return attr
  883. return WSA.FAULT
  884. def GetWSAActionInput(operation):
  885. """Find wsa:Action attribute, and return value or the default."""
  886. attr = operation.input.action
  887. if attr is not None:
  888. return attr
  889. portType = operation.getPortType()
  890. targetNamespace = portType.getTargetNamespace()
  891. ptName = portType.name
  892. msgName = operation.input.name
  893. if not msgName:
  894. msgName = operation.name + 'Request'
  895. if targetNamespace.endswith('/'):
  896. return '%s%s/%s' %(targetNamespace, ptName, msgName)
  897. return '%s/%s/%s' %(targetNamespace, ptName, msgName)
  898. def GetWSAActionOutput(operation):
  899. """Find wsa:Action attribute, and return value or the default."""
  900. attr = operation.output.action
  901. if attr is not None:
  902. return attr
  903. targetNamespace = operation.getPortType().getTargetNamespace()
  904. ptName = operation.getPortType().name
  905. msgName = operation.output.name
  906. if not msgName:
  907. msgName = operation.name + 'Response'
  908. if targetNamespace.endswith('/'):
  909. return '%s%s/%s' %(targetNamespace, ptName, msgName)
  910. return '%s/%s/%s' %(targetNamespace, ptName, msgName)
  911. def FindExtensions(object, kind, t_type=type(())):
  912. if isinstance(kind, t_type):
  913. result = []
  914. namespaceURI, name = kind
  915. return [ item for item in object.extensions
  916. if hasattr(item, 'nodeType') \
  917. and DOM.nsUriMatch(namespaceURI, item.namespaceURI) \
  918. and item.name == name ]
  919. return [ item for item in object.extensions if isinstance(item, kind) ]
  920. def FindExtension(object, kind, t_type=type(())):
  921. if isinstance(kind, t_type):
  922. namespaceURI, name = kind
  923. for item in object.extensions:
  924. if hasattr(item, 'nodeType') \
  925. and DOM.nsUriMatch(namespaceURI, item.namespaceURI) \
  926. and item.name == name:
  927. return item
  928. else:
  929. for item in object.extensions:
  930. if isinstance(item, kind):
  931. return item
  932. return None
  933. class SOAPCallInfo:
  934. """SOAPCallInfo captures the important binding information about a
  935. SOAP operation, in a structure that is easier to work with than
  936. raw WSDL structures."""
  937. def __init__(self, methodName):
  938. self.methodName = methodName
  939. self.inheaders = []
  940. self.outheaders = []
  941. self.inparams = []
  942. self.outparams = []
  943. self.retval = None
  944. encodingStyle = DOM.NS_SOAP_ENC
  945. documentation = ''
  946. soapAction = None
  947. transport = None
  948. namespace = None
  949. location = None
  950. use = 'encoded'
  951. style = 'rpc'
  952. def addInParameter(self, name, type, namespace=None, element_type=0):
  953. """Add an input parameter description to the call info."""
  954. parameter = ParameterInfo(name, type, namespace, element_type)
  955. self.inparams.append(parameter)
  956. return parameter
  957. def addOutParameter(self, name, type, namespace=None, element_type=0):
  958. """Add an output parameter description to the call info."""
  959. parameter = ParameterInfo(name, type, namespace, element_type)
  960. self.outparams.append(parameter)
  961. return parameter
  962. def setReturnParameter(self, name, type, namespace=None, element_type=0):
  963. """Set the return parameter description for the call info."""
  964. parameter = ParameterInfo(name, type, namespace, element_type)
  965. self.retval = parameter
  966. return parameter
  967. def addInHeaderInfo(self, name, type, namespace, element_type=0,
  968. mustUnderstand=0):
  969. """Add an input SOAP header description to the call info."""
  970. headerinfo = HeaderInfo(name, type, namespace, element_type)
  971. if mustUnderstand:
  972. headerinfo.mustUnderstand = 1
  973. self.inheaders.append(headerinfo)
  974. return headerinfo
  975. def addOutHeaderInfo(self, name, type, namespace, element_type=0,
  976. mustUnderstand=0):
  977. """Add an output SOAP header description to the call info."""
  978. headerinfo = HeaderInfo(name, type, namespace, element_type)
  979. if mustUnderstand:
  980. headerinfo.mustUnderstand = 1
  981. self.outheaders.append(headerinfo)
  982. return headerinfo
  983. def getInParameters(self):
  984. """Return a sequence of the in parameters of the method."""
  985. return self.inparams
  986. def getOutParameters(self):
  987. """Return a sequence of the out parameters of the method."""
  988. return self.outparams
  989. def getReturnParameter(self):
  990. """Return param info about the return value of the method."""
  991. return self.retval
  992. def getInHeaders(self):
  993. """Return a sequence of the in headers of the method."""
  994. return self.inheaders
  995. def getOutHeaders(self):
  996. """Return a sequence of the out headers of the method."""
  997. return self.outheaders
  998. class ParameterInfo:
  999. """A ParameterInfo object captures parameter binding information."""
  1000. def __init__(self, name, type, namespace=None, element_type=0):
  1001. if element_type:
  1002. self.element_type = 1
  1003. if namespace is not None:
  1004. self.namespace = namespace
  1005. self.name = name
  1006. self.type = type
  1007. element_type = 0
  1008. namespace = None
  1009. default = None
  1010. class HeaderInfo(ParameterInfo):
  1011. """A HeaderInfo object captures SOAP header binding information."""
  1012. def __init__(self, name, type, namespace, element_type=None):
  1013. ParameterInfo.__init__(self, name, type, namespace, element_type)
  1014. mustUnderstand = 0
  1015. actor = None
  1016. def callInfoFromWSDL(port, name):
  1017. """Return a SOAPCallInfo given a WSDL port and operation name."""
  1018. wsdl = port.getService().getWSDL()
  1019. binding = port.getBinding()
  1020. portType = binding.getPortType()
  1021. operation = portType.operations[name]
  1022. opbinding = binding.operations[name]
  1023. messages = wsdl.messages
  1024. callinfo = SOAPCallInfo(name)
  1025. addrbinding = port.getAddressBinding()
  1026. if not isinstance(addrbinding, SoapAddressBinding):
  1027. raise ValueError, 'Unsupported binding type.'
  1028. callinfo.location = addrbinding.location
  1029. soapbinding = binding.findBinding(SoapBinding)
  1030. if soapbinding is None:
  1031. raise ValueError, 'Missing soap:binding element.'
  1032. callinfo.transport = soapbinding.transport
  1033. callinfo.style = soapbinding.style or 'document'
  1034. soap_op_binding = opbinding.findBinding(SoapOperationBinding)
  1035. if soap_op_binding is not None:
  1036. callinfo.soapAction = soap_op_binding.soapAction
  1037. callinfo.style = soap_op_binding.style or callinfo.style
  1038. parameterOrder = operation.parameterOrder
  1039. if operation.input is not None:
  1040. message = messages[operation.input.message]
  1041. msgrole = opbinding.input
  1042. mime = msgrole.findBinding(MimeMultipartRelatedBinding)
  1043. if mime is not None:
  1044. raise ValueError, 'Mime bindings are not supported.'
  1045. else:
  1046. for item in msgrole.findBindings(SoapHeaderBinding):
  1047. part = messages[item.message].parts[item.part]
  1048. header = callinfo.addInHeaderInfo(
  1049. part.name,
  1050. part.element or part.type,
  1051. item.namespace,
  1052. element_type = part.element and 1 or 0
  1053. )
  1054. header.encodingStyle = item.encodingStyle
  1055. body = msgrole.findBinding(SoapBodyBinding)
  1056. if body is None:
  1057. raise ValueError, 'Missing soap:body binding.'
  1058. callinfo.encodingStyle = body.encodingStyle
  1059. callinfo.namespace = body.namespace
  1060. callinfo.use = body.use
  1061. if body.parts is not None:
  1062. parts = []
  1063. for name in body.parts:
  1064. parts.append(message.parts[name])
  1065. else:
  1066. parts = message.parts.values()
  1067. for part in parts:
  1068. callinfo.addInParameter(
  1069. part.name,
  1070. part.element or part.type,
  1071. element_type = part.element and 1 or 0
  1072. )
  1073. if operation.output is not None:
  1074. try:
  1075. message = messages[operation.output.message]
  1076. except KeyError:
  1077. if self.strict:
  1078. raise RuntimeError(
  1079. "Recieved message not defined in the WSDL schema: %s" %
  1080. operation.output.message)
  1081. else:
  1082. message = wsdl.addMessage(operation.output.message)
  1083. print "Warning:", \
  1084. "Recieved message not defined in the WSDL schema.", \
  1085. "Adding it."
  1086. print "Message:", operation.output.message
  1087. msgrole = opbinding.output
  1088. mime = msgrole.findBinding(MimeMultipartRelatedBinding)
  1089. if mime is not None:
  1090. raise ValueError, 'Mime bindings are not supported.'
  1091. else:
  1092. for item in msgrole.findBindings(SoapHeaderBinding):
  1093. part = messages[item.message].parts[item.part]
  1094. header = callinfo.addOutHeaderInfo(
  1095. part.name,
  1096. part.element or part.type,
  1097. item.namespace,
  1098. element_type = part.element and 1 or 0
  1099. )
  1100. header.encodingStyle = item.encodingStyle
  1101. body = msgrole.findBinding(SoapBodyBinding)
  1102. if body is None:
  1103. raise ValueError, 'Missing soap:body binding.'
  1104. callinfo.encodingStyle = body.encodingStyle
  1105. callinfo.namespace = body.namespace
  1106. callinfo.use = body.use
  1107. if body.parts is not None:
  1108. parts = []
  1109. for name in body.parts:
  1110. parts.append(message.parts[name])
  1111. else:
  1112. parts = message.parts.values()
  1113. if parts:
  1114. # XXX no idea what this is for, but it breaks everything. jrb
  1115. #callinfo.setReturnParameter(
  1116. # parts[0].name,
  1117. # parts[0].element or parts[0].type,
  1118. # element_type = parts[0].element and 1 or 0
  1119. # )
  1120. #for part in parts[1:]:
  1121. for part in parts:
  1122. callinfo.addOutParameter(
  1123. part.name,
  1124. part.element or part.type,
  1125. element_type = part.element and 1 or 0
  1126. )
  1127. return callinfo