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.
 
 
 

304 lines
11 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. from urlparse import urlparse
  10. from ZSI import *
  11. from ZSI.client import *
  12. import weakref
  13. from Utility import DOM
  14. import WSDLTools
  15. class SOAPCallInfo:
  16. """SOAPCallInfo captures the important binding information about a
  17. SOAP operation, in a structure that is easier to work with than
  18. raw WSDL structures."""
  19. def __init__(self, methodName):
  20. self.methodName = methodName
  21. self.inheaders = []
  22. self.outheaders = []
  23. self.inparams = []
  24. self.outparams = []
  25. self.retval = None
  26. encodingStyle = DOM.NS_SOAP_ENC
  27. documentation = ''
  28. soapAction = None
  29. transport = None
  30. namespace = None
  31. location = None
  32. use = 'encoded'
  33. style = 'rpc'
  34. def addInParameter(self, name, type, namespace=None, element_type=0):
  35. """Add an input parameter description to the call info."""
  36. parameter = ParameterInfo(name, type, namespace, element_type)
  37. self.inparams.append(parameter)
  38. return parameter
  39. def addOutParameter(self, name, type, namespace=None, element_type=0):
  40. """Add an output parameter description to the call info."""
  41. parameter = ParameterInfo(name, type, namespace, element_type)
  42. self.outparams.append(parameter)
  43. return parameter
  44. def setReturnParameter(self, name, type, namespace=None, element_type=0):
  45. """Set the return parameter description for the call info."""
  46. parameter = ParameterInfo(name, type, namespace, element_type)
  47. self.retval = parameter
  48. return parameter
  49. def addInHeaderInfo(self, name, type, namespace, element_type=0,
  50. mustUnderstand=0):
  51. """Add an input SOAP header description to the call info."""
  52. headerinfo = HeaderInfo(name, type, namespace, element_type)
  53. if mustUnderstand:
  54. headerinfo.mustUnderstand = 1
  55. self.inheaders.append(headerinfo)
  56. return headerinfo
  57. def addOutHeaderInfo(self, name, type, namespace, element_type=0,
  58. mustUnderstand=0):
  59. """Add an output SOAP header description to the call info."""
  60. headerinfo = HeaderInfo(name, type, namespace, element_type)
  61. if mustUnderstand:
  62. headerinfo.mustUnderstand = 1
  63. self.outheaders.append(headerinfo)
  64. return headerinfo
  65. def getInParameters(self):
  66. """Return a sequence of the in parameters of the method."""
  67. return self.inparams
  68. def getOutParameters(self):
  69. """Return a sequence of the out parameters of the method."""
  70. return self.outparams
  71. def getReturnParameter(self):
  72. """Return param info about the return value of the method."""
  73. return self.retval
  74. def getInHeaders(self):
  75. """Return a sequence of the in headers of the method."""
  76. return self.inheaders
  77. def getOutHeaders(self):
  78. """Return a sequence of the out headers of the method."""
  79. return self.outheaders
  80. class ParameterInfo:
  81. """A ParameterInfo object captures parameter binding information."""
  82. def __init__(self, name, type, namespace=None, element_type=0):
  83. if element_type:
  84. self.element_type = 1
  85. if namespace is not None:
  86. self.namespace = namespace
  87. self.name = name
  88. self.type = type
  89. element_type = 0
  90. namespace = None
  91. default = None
  92. class HeaderInfo(ParameterInfo):
  93. """A HeaderInfo object captures SOAP header binding information."""
  94. def __init__(self, name, type, namespace, element_type=None):
  95. ParameterInfo.__init__(self, name, type, namespace, element_type)
  96. mustUnderstand = 0
  97. actor = None
  98. def callInfoFromWSDL(port, name):
  99. """Return a SOAPCallInfo given a WSDL port and operation name."""
  100. wsdl = port.getService().getWSDL()
  101. binding = port.getBinding()
  102. portType = binding.getPortType()
  103. operation = portType.operations[name]
  104. opbinding = binding.operations[name]
  105. messages = wsdl.messages
  106. callinfo = SOAPCallInfo(name)
  107. addrbinding = port.getAddressBinding()
  108. if not isinstance(addrbinding, WSDLTools.SoapAddressBinding):
  109. raise ValueError, 'Unsupported binding type.'
  110. callinfo.location = addrbinding.location
  111. soapbinding = binding.findBinding(WSDLTools.SoapBinding)
  112. if soapbinding is None:
  113. raise ValueError, 'Missing soap:binding element.'
  114. callinfo.transport = soapbinding.transport
  115. callinfo.style = soapbinding.style or 'document'
  116. soap_op_binding = opbinding.findBinding(WSDLTools.SoapOperationBinding)
  117. if soap_op_binding is not None:
  118. callinfo.soapAction = soap_op_binding.soapAction
  119. callinfo.style = soap_op_binding.style or callinfo.style
  120. parameterOrder = operation.parameterOrder
  121. if operation.input is not None:
  122. message = messages[operation.input.message]
  123. msgrole = opbinding.input
  124. mime = msgrole.findBinding(WSDLTools.MimeMultipartRelatedBinding)
  125. if mime is not None:
  126. raise ValueError, 'Mime bindings are not supported.'
  127. else:
  128. for item in msgrole.findBindings(WSDLTools.SoapHeaderBinding):
  129. part = messages[item.message].parts[item.part]
  130. header = callinfo.addInHeaderInfo(
  131. part.name,
  132. part.element or part.type,
  133. item.namespace,
  134. element_type = part.element and 1 or 0
  135. )
  136. header.encodingStyle = item.encodingStyle
  137. body = msgrole.findBinding(WSDLTools.SoapBodyBinding)
  138. if body is None:
  139. raise ValueError, 'Missing soap:body binding.'
  140. callinfo.encodingStyle = body.encodingStyle
  141. callinfo.namespace = body.namespace
  142. callinfo.use = body.use
  143. if body.parts is not None:
  144. parts = []
  145. for name in body.parts:
  146. parts.append(message.parts[name])
  147. else:
  148. parts = message.parts.values()
  149. for part in parts:
  150. callinfo.addInParameter(
  151. part.name,
  152. part.element or part.type,
  153. element_type = part.element and 1 or 0
  154. )
  155. if operation.output is not None:
  156. message = messages[operation.output.message]
  157. msgrole = opbinding.output
  158. mime = msgrole.findBinding(WSDLTools.MimeMultipartRelatedBinding)
  159. if mime is not None:
  160. raise ValueError, 'Mime bindings are not supported.'
  161. else:
  162. for item in msgrole.findBindings(WSDLTools.SoapHeaderBinding):
  163. part = messages[item.message].parts[item.part]
  164. header = callinfo.addOutHeaderInfo(
  165. part.name,
  166. part.element or part.type,
  167. item.namespace,
  168. element_type = part.element and 1 or 0
  169. )
  170. header.encodingStyle = item.encodingStyle
  171. body = msgrole.findBinding(WSDLTools.SoapBodyBinding)
  172. if body is None:
  173. raise ValueError, 'Missing soap:body binding.'
  174. callinfo.encodingStyle = body.encodingStyle
  175. callinfo.namespace = body.namespace
  176. callinfo.use = body.use
  177. if body.parts is not None:
  178. parts = []
  179. for name in body.parts:
  180. parts.append(message.parts[name])
  181. else:
  182. parts = message.parts.values()
  183. if parts:
  184. callinfo.setReturnParameter(
  185. parts[0].name,
  186. parts[0].element or parts[0].type,
  187. element_type = parts[0].element and 1 or 0
  188. )
  189. for part in parts[1:]:
  190. callinfo.addOutParameter(
  191. part.name,
  192. part.element or part.type,
  193. element_type = part.element and 1 or 0
  194. )
  195. return callinfo
  196. class ServiceProxy:
  197. """A ServiceProxy provides a convenient way to call a remote web
  198. service that is described with WSDL. The proxy exposes methods
  199. that reflect the methods of the remote web service."""
  200. def __init__(self, wsdl, service=None, port=None, tracefile=None,
  201. typesmodule=None, nsdict=None):
  202. if not hasattr(wsdl, 'targetNamespace'):
  203. wsdl = WSDLTools.WSDLReader().loadFromURL(wsdl)
  204. # for item in wsdl.types.items():
  205. # self._serializer.loadSchema(item)
  206. self._service = wsdl.services[service or 0]
  207. self.__doc__ = self._service.documentation
  208. self._port = self._service.ports[port or 0]
  209. self._name = self._service.name
  210. self._wsdl = wsdl
  211. self._tracefile = tracefile
  212. self._typesmodule = typesmodule
  213. self._nsdict = nsdict
  214. binding = self._port.getBinding()
  215. portType = binding.getPortType()
  216. for item in portType.operations:
  217. callinfo = callInfoFromWSDL(self._port, item.name)
  218. method = MethodProxy(self, callinfo)
  219. setattr(self, item.name, method)
  220. def _call(self, name, *args, **kwargs):
  221. """Call the named remote web service method."""
  222. if len(args) and len(kwargs):
  223. raise TypeError(
  224. 'Use positional or keyword argument only.'
  225. )
  226. callinfo = getattr(self, name).callinfo
  227. url = callinfo.location
  228. (protocol, host, uri, query, fragment, identifier) = urlparse(url)
  229. port = 80
  230. if host.find(':') >= 0:
  231. host, port = host.split(':')
  232. params = callinfo.getInParameters()
  233. host = str(host)
  234. port = str(port)
  235. binding = Binding(host=host, tracefile=self._tracefile,
  236. ssl=(protocol == 'https'),
  237. port=port, url=uri, typesmodule=self._typesmodule,
  238. nsdict=self._nsdict)
  239. apply(getattr(binding, callinfo.methodName), args)
  240. #print binding.ReceiveRaw()
  241. return binding.Receive()
  242. class MethodProxy:
  243. """ """
  244. def __init__(self, parent, callinfo):
  245. self.__name__ = callinfo.methodName
  246. self.__doc__ = callinfo.documentation
  247. self.callinfo = callinfo
  248. self.parent = weakref.ref(parent)
  249. def __call__(self, *args, **kwargs):
  250. return self.parent()._call(self.__name__, *args, **kwargs)