PageRenderTime 24ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/ncluasoap.lua

http://github.com/leandrojsa/weblua
Lua | 281 lines | 127 code | 22 blank | 132 comment | 32 complexity | 40f89f0458f02dddc19c4f1aae1542d0 MD5 | raw file
  1. ---Módulo NCLua SOAP v0.5.6.6: Módulo desenvolvido inteiramente em Lua, para acesso a
  2. --WebServices SOAP em aplicações de TV Digital.<p/>
  3. --Utiliza o módulo LuaXML, disponível em <a href="http://lua-users.org/wiki/LuaXml">http://lua-users.org/wiki/LuaXml</a>,
  4. --que foi adaptado para Lua 5.x e adicionado parâmetro parseAttributes
  5. --ao método xmlParser.parse para indicar se os atributos das tags
  6. --devem ser processados ou não. <p/>
  7. --Licença: <a href="http://creativecommons.org/licenses/by-nc-sa/2.5/br/">http://creativecommons.org/licenses/by-nc-sa/2.5/br/</a>
  8. --@author Manoel Campos da Silva Filho - <a href="http://manoelcampos.com">http://manoelcampos.com</a>
  9. --@class module
  10. --http://www.xml.com/pub/a/2000/11/29/schemas/part1.html?page=8
  11. --http://www.developerfusion.com/article/3720/understanding-xml-namespaces/5/
  12. --http://www.quackit.com/xml/tutorial/xml_default_namespace.cfm
  13. require "util"
  14. require "tcp"
  15. require "http"
  16. dofile("lib/LuaXML/xml.lua")
  17. dofile("lib/LuaXML/handler.lua")
  18. local _G, util, tcp, http, print, string, table, pairs,
  19. simpleTreeHandler, xmlParser, type, tostring, error
  20. =
  21. _G, util, tcp, http, print, string, table, pairs,
  22. simpleTreeHandler, xmlParser, type, tostring, error
  23. module "ncluasoap"
  24. local userAgent = "ncluasoap/0.5.6.6"
  25. ---Converte uma tabela lua para uma string que representa um trecho de código XML
  26. --@param tb Tabela a ser convertida
  27. --@param level Apenas usado internamente, quando a função
  28. --é chamada recursivamente, para imprimir espaços e
  29. --representar os níveis dentro da tabela.
  30. --@param elevateAnonymousSubTables Se igual a true, quando encontrada uma sub-tabela sem nome dentro da tabela
  31. --(tendo sido definida apenas como {chave1 = valor1, chave2 = valor2, chaveN = valorN}
  32. --ao invés de nomeSubTabela = {chave1 = valor1, chave2 = valor2, chaveN = valorN})
  33. --os elementos da sub-tabela serão consideradas como se estivessem diretamento dentro da tabela
  34. --a qual a sub-tabela pertence, e não que estejam dentro da sub-tabela.
  35. --Tais sub-tabelas não tem um nome para a chave, e sim um índice definido
  36. --automaticamente pelo compilador Lua. O parâmetro é opcional e seu valor default é false.
  37. --O uso do valor true é útil quando tem-se sub-tabelas contendo apenas um campo,
  38. --onde colocou-se tal campo dentro da sub-tabela sem nome, apenas para que, ao ser processada a tabela
  39. --principal, os campos sejam acessados na ordem em que foram definidos, e não em ordem
  40. --arbitrária definida pela função pairs (usada internamente nesta função).
  41. --A ordem do processamento dos campos de uma tabela é importante no caso do
  42. --processamento da tabela de parâmetros a serem passados a um WebService,
  43. --pois WSs PHP feitos como a biblioteca NuSOAP, não
  44. --verificam o nome dos parâmetros, e sim a ordem em que são passados.
  45. --Assim, o comportamento padrão de acesso aos elementos de uma tabela
  46. --não garante que os campos serão acessados na mesma ordem em que
  47. --foram definidos, podendo fazer com que sejam passados os valores
  48. --trocados para o WS sendo acessado.
  49. --@return Retorna a string com as tags XML geradas
  50. --a partir dos itens da tabela
  51. --@param tableName Nome da variável table sendo passada para a função.
  52. --Este parâmetro é opcional e seu valor é utilizado apenas quando
  53. --é passada uma table em formato de um vetor (array) onde
  54. -- existem índices, não existindo chaves nomeadas (como ocorre em um registro/struct).
  55. --Assim, para cada posição no vetor, será gerada uma tag com o nome do mesmo,
  56. --contendo os dados de cada posição. Isto é utilizado
  57. --quando o método no Web Service a ser chamado possuir um vetor como parâmetro.
  58. --Logo, se existir um parâmetro, no método do WS,
  59. --de nome vet e do tipo array, a função tableToXml gerará
  60. --um código XML como <vet>valor1</vet><vet>valor2</vet><vet>valorN</vet>
  61. --para passar tal vetor (table lua) ao método do Web Service.
  62. local function tableToXml(tb, level, elevateAnonymousSubTables, tableName)
  63. level = level or 1
  64. local spaces = string.rep(' ', level*2)
  65. local xmltb = {}
  66. for k, v in pairs(tb) do
  67. if type(v) == "table" then
  68. if type(k) == "number" then
  69. --Se o nome da chave da sub-tabela for um número,
  70. --é porque esta sub-tabela não possui um nome, tendo sido definida
  71. --como {chave1 = valor1, chave2 = valor2, chaveN = valorN}
  72. --ao invés de nomeSubTabela = {chave1 = valor1, chave2 = valor2, chaveN = valorN}.
  73. --Então, se é pra elevar esta sub-tabela anônima, processa seus campos como
  74. --se estivessem fora da sub-tabela, como explicado na documentação desta função.
  75. if elevateAnonymousSubTables then
  76. table.insert(xmltb, spaces..tableToXml(v))
  77. else
  78. table.insert(xmltb,
  79. spaces..'<'..tableName..'>\n'..tableToXml(v)..
  80. '\n'..spaces..'</'..tableName..'>')
  81. end
  82. else --se o elemento é uma tabela e sua chave tem um nome definido
  83. level = level + 1
  84. --Obtém o nome da primeira chave da sub-tabela v. Se o tipo dela for numérico,
  85. --considera que a mesma é um array (vetor), assim, para cada elemento existente,
  86. --será criada uma tag com o nome da table.
  87. --Por isto, aqui a função tableToXml é chamada recursivamente,
  88. --passando um valor para o parâmetro tableName.
  89. if type(util.getFirstKey(v)) == "number" then --
  90. table.insert(xmltb, spaces..tableToXml(v, level, false, k))
  91. else
  92. --Senão, considera que a table está em formato de struct,
  93. --logo, possui chaves nomeadas. Desta forma, cria uma tag com o nome da table
  94. --e inclui seus elementos como sub-tags contendo seus respectivos nomes.
  95. table.insert(
  96. xmltb,
  97. spaces..'<'..k..'>\n'.. tableToXml(v, level)..
  98. '\n'..spaces..'</'..k..'>')
  99. end
  100. end
  101. else
  102. --Se o parâmetro tableName foi informado, é porque a table passada
  103. --está estruturada como um array (vetor), assim, para cada elemento dela
  104. --deve ser criada uma tag com o nome da table (tableName)
  105. if tableName then
  106. k = tableName
  107. else
  108. --Se o elemento não for uma tabela mas o nome da sua chave for um índice numérico,
  109. --deve-se incluir uma letra qualquer antes do nome da chave, pois alguns
  110. --WS (como os em PHP) não suportam chaves numéricas no XML.
  111. --Isto é feito apenas quando a função é chamada para gerar o trecho XML para
  112. --os parâmetros de entrada do WS, quando elevateAnonymousSubTables é true.
  113. if type(k) == "number" and elevateAnonymousSubTables then
  114. k = "p" .. k --p é o prefixo de param (poderia ser usado qualquer caractere alfabético)
  115. end
  116. end
  117. table.insert(xmltb, spaces..'<'..k..'>'..tostring(v)..'</'..k..'>')
  118. end
  119. end
  120. return table.concat(xmltb, "\n")
  121. end
  122. ---Envia uma requisição SOAP para realizar a chamada de um método remoto vai HTTP
  123. --@param msgTable Tabela contendo os parâmetros
  124. --da requisição, devendo ter o seguinte formato:<br/>
  125. --msgTable = {<br/>
  126. --&nbsp;&nbsp;&nbsp;address = "URL do serviço (não é o wsdl). Pode-se incluir um número de porta na mesma.",<br/>
  127. --&nbsp;&nbsp;&nbsp;namespace = "namespace, informado no wsdl",<br/>
  128. --&nbsp;&nbsp;&nbsp;operationName = "método remoto que deseja-se acessar",<br/>
  129. --&nbsp;&nbsp;&nbsp;--Parâmetros de entrada. Se não existirem parâmetros de entrada,
  130. -- o campo deve ser omitido<br/>
  131. --&nbsp;&nbsp;&nbsp;params = {<br/>
  132. --&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;paramName1=value1,<br/>
  133. --&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;paramName2=value2,<br/>
  134. --&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;paramNameN=valueN<br/>
  135. --&nbsp;&nbsp;&nbsp;}<br/>
  136. --&nbsp;}<br/>
  137. --@param callback Função de callback a ser executada quando
  138. --a resposta da requisição for obtida. A mesma deve possuir
  139. --em sua assinatura, um parâmetro response, que conterá
  140. --o retorno da chamada da função remota.
  141. --@param soapVersion Versão do protocolo SOAP a ser utilizado.
  142. --Os valores permitidos são 1.1 e 1.2. Se omitido, será usado 1.2
  143. --Alguns WebServices (como os em ASP.NET, de extensão asmx)
  144. --suportam diferentes versões do SOAP (como 1.1 e 1.2),
  145. --mas pode ser que isto não seja verdade para todos os serviços.
  146. --Assim, precisará ser informado qual a versão do protocolo a ser utilizada.
  147. --@param port Porta a ser utilizada para a conexão. O padrão é 80, no caso do valor ser omitido.
  148. --A porta também pode ser especificada diretamente na URL (campo address do parâmetro msgTable).
  149. --Se for indicada uma porta e aqui no parâmetro port, a porta da url é que será utilizada e a do parâmetro port será ignorada.
  150. --@param boolean externalXsd Indica se o Web Service utiliza um arquivo externo (xsd) para as definições de tipos (XML Schema Definition).
  151. --O formato da requisição SOAP é dependente disto. A maioria dos Web Services construídos, em diversas linguagens,
  152. --inclui as definições de tipo diretamente no arquivo WSDL. Os Web Services que usam um xsd externo possuem
  153. --uma tag xsd:import em seu WSDL. Enquanto o NCLua SOAP não descobre automaticamente esta informação,
  154. --é necessário que a mesma seja informada pelo desenvolvedor que for usar o módulo.
  155. --Web Services feitos com a ferramenta Netbeans, utilizando a biblioteca JAX-WS (pelo menos em alguma
  156. --de suas versões) faz uso de um xsd externo. Neste caso, este parâmetro externalXsd deve ser true.
  157. --O valor padrão do parâmetro é false.
  158. --@param httpUser Nome de usuário para realizar autenticação HTTP, em caso de WS que utilizam tal recurso. Parâmetro Opcional.
  159. --@param httpPasswd Senha para realizar autenticação HTTP, em caso de WS que utilizam tal recurso. Parâmetro Opcional.
  160. --@param soapHeader String contendo cabeçalho SOAP a serem enviado na requisição.
  161. --Tal cabeçalho é uma tag contendo valores a serem passados ao Web Service. Opcional.
  162. function call(msgTable, callback, soapVersion, port, externalXsd, httpUser, httpPasswd, soapHeader)
  163. if soapVersion == nil or soapVersion == "" then
  164. soapVersion = "1.2"
  165. end
  166. if soapVersion ~= "1.1" and soapVersion ~= "1.2" then
  167. error("soapVersion deve ser 1.1 ou 1.2")
  168. end
  169. --nsPrefix = Namespace Prefix
  170. local nsPrefix = ""
  171. if soapVersion == "1.1" then
  172. nsPrefix = "soap"
  173. elseif soapVersion == "1.2" then
  174. nsPrefix = "soap12"
  175. end
  176. local xmltb = {}
  177. --SOAP 1.2 message
  178. --table.insert(xmltb, '')
  179. table.insert(xmltb, '<?xml version="1.0"?>')
  180. table.insert(xmltb, '<'..nsPrefix..':Envelope ')
  181. table.insert(xmltb, ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ')
  182. table.insert(xmltb, ' xmlns:xsd="http://www.w3.org/2001/XMLSchema" ')
  183. if soapVersion == "1.1" then
  184. table.insert(xmltb,
  185. ' xmlns:'..nsPrefix..'="http://schemas.xmlsoap.org/soap/envelope/" ')
  186. elseif soapVersion == "1.2" then
  187. table.insert(xmltb,
  188. ' xmlns:'..nsPrefix..'="http://www.w3.org/2003/05/soap-envelope" ')
  189. end
  190. local serviceNsPrefix = ""
  191. if externalXsd then
  192. serviceNsPrefix = "ns1"
  193. table.insert(xmltb, ' xmlns:'..serviceNsPrefix..'="'..msgTable.namespace..'" ')
  194. end
  195. table.insert(xmltb, '>')
  196. --Se foi passado um cabeçalho SOAP a ser anexado na requisição,
  197. --anexa tais dados dentro da tag Header
  198. if soapHeader then
  199. table.insert(xmltb, ' <'..nsPrefix..':Header>')
  200. table.insert(xmltb, soapHeader)
  201. table.insert(xmltb, ' </'..nsPrefix..':Header>')
  202. end
  203. table.insert(xmltb, ' <'..nsPrefix..':Body>')
  204. if externalXsd then
  205. table.insert(xmltb, ' <'..serviceNsPrefix..':'..msgTable.operationName..'>')
  206. else
  207. table.insert(xmltb, ' <'..msgTable.operationName..' xmlns="'..msgTable.namespace..'">')
  208. end
  209. if msgTable.params then
  210. --Percorre a lista de parâmetros da mensagem e gera
  211. --tags XML para cada item existente na tabela msgTable.params
  212. --O parâmetro elevateAnonymousSubTables é definido como true para que
  213. --sub-tabelas de msgTable.params que não tenham nome,
  214. --tenham seu único campo processado como se estivesse fora da tabela anônima.
  215. --Isto permite que os elementos da tabela de parâmetros sejam
  216. --acessados na mesma ordem em que foram definidas as sub-tabelas
  217. --anônimas.
  218. --Considera-se que estas tabelas anônimas, caso existam, terão
  219. --apenas um campo, pois cada elemento da tabela de parâmetros
  220. --a serem passados ao WS deve conter apenas um valor simples.
  221. --O uso de parâmetros com valores compostos não é suportado
  222. --pelo NCLua SOAP. Veja mais detalhes na função tableToXml.
  223. --O valor 3 para o parâmetro level é usado apenas para que seja dada a correta
  224. --identação na inclusão das tags XML referentes aos parâmetros da chamada SOAP.
  225. table.insert(xmltb, tableToXml(msgTable.params, 3, true))
  226. end
  227. if externalXsd then
  228. table.insert(xmltb, ' </'..serviceNsPrefix..':'..msgTable.operationName..'>')
  229. else
  230. table.insert(xmltb, ' </'..msgTable.operationName..'>')
  231. end
  232. table.insert(xmltb, ' </'..nsPrefix..':Body>')
  233. table.insert(xmltb, '</'..nsPrefix..':Envelope>')
  234. local xml = table.concat(xmltb, '\n')
  235. print(xml)
  236. local httpContentType = ''
  237. if soapVersion == "1.1" then
  238. local soapAction, separator = "", ""
  239. if string.sub(msgTable.namespace, #msgTable.namespace) ~= '/' then
  240. separator = '/'
  241. end
  242. soapAction = msgTable.namespace..separator..msgTable.operationName
  243. httpContentType =
  244. 'Content-Type: text/xml; charset=utf-8\n' ..
  245. 'SOAPAction: "'..soapAction..'"'
  246. elseif soapVersion == "1.2" then
  247. httpContentType = 'Content-Type: application/soap+xml; charset="utf-8"'
  248. end
  249. local function getHttpResponse(header, body)
  250. print(""); print(body)
  251. if callback then
  252. callback(header, body)
  253. end
  254. end
  255. local url = msgTable.address
  256. --(url, callback, method, params, userAgent, headers, user, password, port)
  257. http.request(url, getHttpResponse, "POST", xml, userAgent,
  258. httpContentType, httpUser, httpPasswd, port)
  259. end