BlackTDN Search

quinta-feira, 5 de maio de 2011

Protheus :: Rompendo Limites e Barreiras (open your mind)

Rodrigo entrou em uma roubada. Perguntou-me: "..иαldσ estou com problemas para gerar um client de um WebService no Protheus, não sei se é minha instalação. Será que você poderia me ajudar?" (foi + ou - assim ... rs)

manda-o-link-aew (respondi eu). e;

taí: http://201.48.221.162:2000/ServiceFuturePMS/service?WSDL

Testei.... hummmmm.... Olha a mensagem emitida pelo IDE:

Estabelecendo conexão com o server...
Por favor aguarde. Obtendo descrição do WebService...
Erro na obtenção da descrição do WebService: 
Invalid function return for WSDLSource Call.
Finalizando conexão com o server...
Ok
Bem... não me ajudou muito.... Opa.... "Invalid function return for WSDLSource Call".... hummmmmm.... WSDLSource... acho que é esse carinha aqui quem tenta gerar o client.... Vamos executá-lo diretamente.... Sintaxe: WSDLSource( cUrl ) : [lSucess][cSource/cError].

WSDLSource( "http://201.48.221.162:2000/ServiceFuturePMS/service?WSDL" )
NIL
Retorno NIL, estava eperando um Array.... Vamos ver o Console do server...

[FATAL][SERVER] [Thread 1356] [THROW] String size overflow! at file c:\advtec9_101202a\lib_base\stringz.hpp line 355

String size overflow! (Oops: Erro, erro no servidor, Bad, bad server ).....

Rodrigo, seguinte, o protheus "p...". A restrição no tamanho da string imposta pelo pessoal de Tecnologia do Protheus não vai deixar que gere o seu Client.

Tem alguma solução? (perguntou ele).... e a resposta foi: 3 Possíveis soluções.

1 ) Abrir um chamado da Totvs/Microsiga e solicitar que ampliem o limite da string;
2 ) Entrar em contato com o Desenvolvedor do WebService e pedir para que ele divida o WS em Vários outros WS ; e
3 ) Usar uma aplicação externa para resolver o WS e retornar a informação ao Protheus. Testei no Windows® PowerShell® e funciona que é uma beleza.

O que acha?

"O pessoal aqui não acredita que essa ultima solução funcione. Tem como mandar um exemplo simples? Tem o Método CountUsers que retorna o número de usuários."....

heheheheh.... demorô.....

Segue o Código:

#INCLUDE "APWEBSRV.CH"
#INCLUDE "PROTHEUS.CH"
#INCLUDE "TRYEXCEPTION.CH"
/*/
 WSSERVICE: u_FuturePMS
 Autor:  Marinaldo de Jesus
 Data:  05/05/2011
 Descricao: Usar PowerShell para Resolver WS nao suportado pelo Protheus em Funcao de Limitacao da Linguagem 
/*/
WSSERVICE u_FuturePMS DESCRIPTION "Integracao Protheus x PowerShell x FuturePMS" NAMESPACE "http://localhost/u_futurepms.apw" 
 
 WSDATA CountUser AS INTEGER
 WSDATA PSException AS BOOLEAN

 WSMETHOD CountUsers DESCRIPTION "Retornar o Numero de Usuarios do ServiceFuturePMS"

ENDWSSERVICE

/*/
 WSMETHOD: CountUsers
 Autor:  Marinaldo de Jesus
 Data:  05/05/2011
 Descricao: Encapsular a Chamada ao Metodo CountUsers do WS ServiceFuturePMS executando-o atraves do PS e retornando o Resultado
/*/
WSMETHOD CountUsers WSRECEIVE PSException WSSEND CountUser WSSERVICE u_FuturePMS

 Local aFileErase := {}
 
 Local cCRLF   := CRLF
 Local cPsHome  := ""
 Local cPsScript  := ""
 Local cRootPath  := ""
 Local cPSFileSript := ""
 Local cPathFileXml := ""
 Local cFullPathXml := ""
 Local cFullPathPS1 := ""
 Local cWsResultPath := "\PPSIntWs\"
 Local cWSFileResult := ""

 Local cXmlError  := ""
 Local cXmlWarning := ""

 Local lWsMethodRet := .T.

 Local oXml
 Local oException

 TRYEXCEPTION

  cRootPath := GetSrvProfString( "RootPath" , "" )
  IF ( SubStr( cRootPath , -1 ) == "\" )
   cRootPath := SubStr( cRootPath , 1 , Len( cRootPath ) - 1 )
  EndIF 

  IF !lIsDir( cWsResultPath )
   MakeDir( cWsResultPath )
   IF !lIsDir( cWsResultPath )
    ExUserException( "Impossível Criar o Diretório: " + cWsResultPath ) 
   EndIF
  EndIF
  
  cWSFileResult  := CriaTrab( NIL , .F. )
  cPathFileXml  := ( cWsResultPath + cWSFileResult ) 
  While ( File( cPathFileXml + ".xml" ) .or. File( cPathFileXml + ".ps1" ) )
   cWSFileResult := CriaTrab( NIL , .F. )
   cPathFileXml := ( cWsResultPath + cWSFileResult ) 
  End While

  cPSFileSript  := ( cWSFileResult + ".ps1" )

  cPathFileXml  += ".xml"
  aAdd( aFileErase , cPathFileXml )

  cFullPathXml  := ( cRootPath + cPathFileXml )
  cPSFileSript  := ( cWsResultPath + cPSFileSript )
  cFullPathPS1  := ( cRootPath + cPSFileSript )

  aAdd( aFileErase , cPSFileSript )

  DEFAULT Self:PSException := .F.
  DEFAULT PSException   := Self:PSException

  /*/
   Para que o "script" possa ser executado faz-se necessario autoriza-lo atraves do Windows PowerShell,   sendo
   assim, execute o Windows PowerShell e digite o seguinte comando: 
   
   get-help about_signing. 
   
   Esse comando ira listar as opcoes de "Diretivas de Assinatura e de Execucao" de "Script". Leia-as atentamente. 
   
   Mas se quiser pular esse passo (eu nao recomendo) digite o seguinte comando abaixo no Windows PowerShell: 
   
   Set-ExecutionPolicy Unrestricted. 
   
   Esse comando fara que qualquer "Script" seja executado em sua máquina (mas atente para os itens de   seguranca 
   listados aa partir do comando get-help about_signing).

  /*/

  cPsScript    :=  " [int]$CountUserResult = 0"
  cPsScript    += cCRLF
  cPsScript    += " [boolean]$CountUserResultSpecified = $True"
  cPsScript    += cCRLF
  cPsScript    += " $ws = New-WebServiceProxy -URI http://201.48.221.162:2000/ServiceFuturePMS/service?WSDL"
  cPsScript    += cCRLF
  cPsScript    += " $ws.CountUsers( [ref]$CountUserResult , [ref]$CountUserResultSpecified )"
  cPsScript    += cCRLF
  cPsScript    += " [string]$PSOutFile = '" + cFullPathXml + "'"
  cPsScript    += cCRLF
  cPsScript    += " $xml = " + '"' + "$CountUserResult" + '"'
  cPsScript    += cCRLF
  cPsScript    += " $xml | Out-File $PSOutFile"
  cPsScript    += cCRLF

  IF ( PSException )
   cPsScript   := AddPsException( @cPsScript , @cCRLF )
  EndIF

  MemoWrite( cPSFileSript , cPsScript )
        
  cPsHome := AllTrim( GetSrvProfString( "PSHome" , "C:\WINDOWS\system32\WindowsPowerShell\v1.0\" ) )
  IF !( SubStr( cPsHome , -1 ) == "\" )
   cPsHome += "\"
  EndIF

  IF !( WaitRunSrv( cPsHome + "powershell.exe " + cFullPathPS1 , .T. , cPsHome ) )
      ExUserException( "Impossivel Executar o PowerShell" )
  EndIF

  IF !File( cPathFileXml )
   ExUserException( "XML não carregado: " + cPathFileXml ) 
  EndIF

  oXml    := XmlParserFile( @cPathFileXml , "_" , @cXmlError , @cXmlWarning )

  IF !( ValType( oXml ) == "O" )
   IF !Empty( cXmlError )
    ExUserException( cXmlError ) 
   EndIF 
   ExUserException( "Impossovel Carregar o XML: " + cPathFileXml ) 
  EndIF

  Self:CountUser  := Val( oXml:_Result:_CountUser:Text )
  CountUser   := Self:CountUser

 CATCHEXCEPTION USING oException
 
  lWsMethodRet  := .F.

  SetSoapFault( ProcName() , CaptureError() )

 ENDEXCEPTION

 aEval( aFileErase , { |cFile| IF( File( cFile ) , fErase( cFile ) , NIL ) } )

Return( lWsMethodRet )

/*/
 WSMETHOD: AddPsException
 Autor:  Marinaldo de Jesus
 Data:  05/05/2011
 Descricao: Adicionar Tratamento de Excecao no Script em PowerShell
/*/
Static Function AddPsException( cPsScript , cCRLF )

 Local cPSException := ""
 Local cMsgException := " 'Não foi possível conectar-se a http://201.48.221.162:2000/ServiceFuturePMS/service?WSDL'"

 cPSException  := "try"
 cPSException  += cCRLF
 cPSException  += "{"
 cPSException  += cCRLF
 cPSException  += cPsScript
 cPSException  += cCRLF
 cPSException  += "}"
 cPSException  += cCRLF
 cPSException  += "catch"
 cPSException  += cCRLF
 cPSException  += "{"  
 cPSException  += cCRLF
 cPSException  += cMsgException
 cPSException  += cCRLF
 cPSException  += "}"
 cPSException  += cCRLF

Return( cPSException )


e o Client gerado a partir dele:

#INCLUDE "PROTHEUS.CH"
#INCLUDE "APWEBSRV.CH"

/* ===============================================================================
WSDL Location    http://200.143.193.75:8081/ws/U_FUTUREPMS.apw?WSDL
Gerado em        05/05/11 09:58:21
Observações      Código-Fonte gerado por ADVPL WSDL Client 1.101007
                 Alterações neste arquivo podem causar funcionamento incorreto
                 e serão perdidas caso o código-fonte seja gerado novamente.
=============================================================================== */

User Function _WHYOFUC ; Return  // "dummy" function - Internal Use 

/* -------------------------------------------------------------------------------
WSDL Service WSU_FUTUREPMS
------------------------------------------------------------------------------- */

WSCLIENT WSU_FUTUREPMS

 WSMETHOD NEW
 WSMETHOD INIT
 WSMETHOD RESET
 WSMETHOD CLONE
 WSMETHOD COUNTUSERS

 WSDATA   _URL                      AS String
 WSDATA   _HEADOUT                  AS Array of String
 WSDATA   lPSEXCEPTION              AS boolean
 WSDATA   nCOUNTUSERSRESULT         AS integer

ENDWSCLIENT

WSMETHOD NEW WSCLIENT WSU_FUTUREPMS
::Init()
If !FindFunction("XMLCHILDEX")
 UserException("O Código-Fonte Client atual requer os executáveis do Protheus Build [7.00.101202A-20110330] ou superior. Atualize o Protheus ou gere o Código-Fonte novamente utilizando o Build atual.")
EndIf
Return Self

WSMETHOD INIT WSCLIENT WSU_FUTUREPMS
Return

WSMETHOD RESET WSCLIENT WSU_FUTUREPMS
 ::lPSEXCEPTION       := NIL 
 ::nCOUNTUSERSRESULT  := NIL 
 ::Init()
Return

WSMETHOD CLONE WSCLIENT WSU_FUTUREPMS
Local oClone := WSU_FUTUREPMS():New()
 oClone:_URL          := ::_URL 
 oClone:lPSEXCEPTION  := ::lPSEXCEPTION
 oClone:nCOUNTUSERSRESULT := ::nCOUNTUSERSRESULT
Return oClone

// WSDL Method COUNTUSERS of Service WSU_FUTUREPMS

WSMETHOD COUNTUSERS WSSEND lPSEXCEPTION WSRECEIVE nCOUNTUSERSRESULT WSCLIENT WSU_FUTUREPMS
Local cSoap := "" , oXmlRet

BEGIN WSMETHOD

cSoap += ''
cSoap += WSSoapValue("PSEXCEPTION", ::lPSEXCEPTION, lPSEXCEPTION , "boolean", .T. , .F., 0 , NIL, .T.) 
cSoap += ""

oXmlRet := SvcSoapCall( Self,cSoap,; 
 "http://localhost/u_futurepms.apw/COUNTUSERS",; 
 "DOCUMENT","http://localhost/u_futurepms.apw",,"1.031217",; 
 "http://200.143.193.75:8081/ws/U_FUTUREPMS.apw")

::Init()
::nCOUNTUSERSRESULT  :=  WSAdvValue( oXmlRet,"_COUNTUSERSRESPONSE:_COUNTUSERSRESULT:TEXT","integer",NIL,NIL,NIL,NIL,NIL,NIL) 

END WSMETHOD

oXmlRet := NIL
Return .T.


Compile o WSServer, gere o Client baseado na sua URL, teste e me diga se funciona ou não.... Foi um exemplo bem simples. Poderia ter retornado o xml diretamente, mas nesse caso não faria sentido.

Eu testei aqui, e olha o resultado.

NameSpace  http://localhost/u_futurepms.apw  
  URL Location  http://200.143.193.75:8081/ws/  
  Nome do Serviço  U_FUTUREPMS  
  Método do Serviço  COUNTUSERS  
 
Resposta da Requisição SOAP 
  

   
      
         1
      
   



Bendita WaitRunSrv() e salve o Windows® PowerShell® (a Totvs bem podia contratar o Engenheiro que planejou o novo Shell do Windows® para reescrever a tecnologia Protheus/by you... o que acham ... rs)

Considerações:

1 ) O Pessoal da Tecnologia Protheus deveria rever essa limitação do tamanho da string no protheus;
2 ) AppServer64 já.

Para baixar os códigos utilizados neste "post", cliquem aqui.


[]s
иαldσ dj

(simples assim)

3 comentários:

  1. Grande Naldo! Mestre no que faz... Para quem pensou que esse nosso desafio nao daria em nada hein!? hehehehe... é como eu ja disse ate para o Bindo... Marinaldo de Jesus é o cara... o resto...bem... o resto é programador... hehehe
    É isso ae Naldo... vc domina e creio q todos que acompanham devia te-lo como exemplo... analizar... modelar.... desenvolver... Se isso fosse de fato feito, talvez o Protheus nao seria uma colcha de retalho! Grande abraço e obrigado pelo desafio!

    ResponderExcluir
  2. Naldo, blz?

    Cara... aproveitando seu tópico... to com um problema aqui... será que você poderia me ajudar?

    Seguinte... gerei uma função client de webservice no Protheus que foi compilada certinho... desenvolvi uma função para chamar esse client e blz... tudo funcionava ok.

    Porém agora mudaram a URL do webservice e tive que alterar minha função client... pra apontar pra essa nova URL... só que agora, na execução do Protheus, retorna a seguinte mensagem:

    "The Web application at http://testehh80.empresaX.com.br/formularios could not be found. Verify that you have typed the URL correctly. If the URL should be serving existing content, the system administrator may need to add a new request URL mapping to the intended application."

    Só que o endereço do webservice que configurei na função não é esse... segue o código que escrevi abaixo...

    oXmlRet := SvcSoapCall( Self,cSoap,;
    "http://tempuri.org/GetCadastrosPendentes",;
    "DOCUMENT","http://tempuri.org/",,,;
    "http://arasrvvirshp002:85/EmpresaXWsSpMicInt/WsSPMicData.asmx")

    O mais engraçado é que essa URL ta funcionando legal no browser do IE e o IDE do Protheus também tá gerando a função desse client certinho.

    Enfim... não consegui descobrir o motivo pelo qual o Protheus não chama o webservice do endereço que to passando... e devolve essa mensagem de erro apontando pra esse testehh80.empresaX.

    Você pode me ajudar nesse problema?

    Obrigado e abraços!

    ResponderExcluir
    Respostas
    1. Perguntas-Respostas:

      1) Quando regerou o Client utilizou o mesmo arquivo fonte ou gerou um novo? Já que está apenas atualizado deveria manter o mesmo arquivo fonte limpando-o antes de gerar o novo client.

      2) Por via das dúvidas (não que isso seja necessário) você excluiu do Projeto e, consequentemente, do RPO o código do Client antigo antes da geração do novo?

      Com as informações passadas, as perguntas-respostas acima talvez resolvam o seu "problema". Caso contrário necessitarei de mais informações.

      Uma forma de descobrir se, de fato o seu RPO está compilado com a última versão é verificando via "Object Inspector" ou utilizando-se das funções existentes no post:

      Protheus :: Advpl :: Usando GetFuncArray, GetApoInfo e VarInfo ( http://goo.gl/k9Fea ).

      Se os itens acima elencados não solucionarem o seu problema envie-me o código do client (o novo) e o seu RPO para mail@blacktdn.com.br. Dessa forma poderei comparar ambos os códigos.

      Excluir