quinta-feira, 19 de agosto de 2010

Consumindo um WebService usando ADVPL

Olá Pessoal, vou mostrar um exemplo de como consumir um webservice usando o advpl. Eu achei um site que possui alguns webservices que podem ser consumidos para teste. o site é http://www.webservicex.net/WCF/webServices.aspx. Este site possui vários webservices, eu escolhi para demonstrar a vocês um que converte byte em todas as outras unidades (Kilobyte, Gigabyte, TeraByte, PetaByte) ou vice-versa. O WebService que vamos usar chama-se ComputerUnit, veja http://www.webservicex.net/WCF/ServiceDetails.aspx?SID=68 para ver os detalhes. Nesta pagina ele mostra qual é o endereço para gerar o client deste serviço (http://www.webservicex.net/ConvertComputer.asmx?wsdl). Este WS recebe 3 parâmetros e retorna 1 parâmetro.
Primeiramente vamos gerar o client deste WS no TotvsDevStudio (IDE). Para isso crie um arquivo novo, vá até o menu Ferramentas - Gerar Cliente WebServices. Na janela que será exibida, informe o endereço do WSDL deste serviço que é http://www.webservicex.net/ConvertComputer.asmx?wsdl. O TotvsDevStudio criará automaticamente o client do WS, conforme abaixo.

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

/* ===============================================================================
WSDL Location    http://www.webservicex.net/ConvertComputer.asmx?wsdl
Gerado em        08/19/10 10:42:26
Observações      Código-Fonte gerado por ADVPL WSDL Client 1.090116
                 Alterações neste arquivo podem causar funcionamento incorreto
                 e serão perdidas caso o código-fonte seja gerado novamente.
=============================================================================== */

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

/* -------------------------------------------------------------------------------
WSDL Service WSComputerUnit
------------------------------------------------------------------------------- */

WSCLIENT WSComputerUnit

    WSMETHOD NEW
    WSMETHOD INIT
    WSMETHOD RESET
    WSMETHOD CLONE
    WSMETHOD ChangeComputerUnit

    WSDATA   _URL                      AS String
    WSDATA   nComputerValue            AS double
    WSDATA   oWSfromComputerUnit       AS ComputerUnit_Computers
    WSDATA   oWStoComputerUnit         AS ComputerUnit_Computers
    WSDATA   nChangeComputerUnitResult AS double

ENDWSCLIENT

WSMETHOD NEW WSCLIENT WSComputerUnit
::Init()
If !FindFunction("XMLCHILDEX")
    UserException("O Código-Fonte Client atual requer os executáveis do Protheus Build [7.00.090818P-20100630] ou superior. Atualize o Protheus ou gere o Código-Fonte novamente utilizando o Build atual.")
EndIf
If val(right(GetWSCVer(),8)) < 1.040504
    UserException("O Código-Fonte Client atual requer a versão de Lib para WebServices igual ou superior a ADVPL WSDL Client 1.040504. Atualize o repositório ou gere o Código-Fonte novamente utilizando o repositório atual.")
EndIf
Return Self

WSMETHOD INIT WSCLIENT WSComputerUnit
    ::oWSfromComputerUnit := ComputerUnit_COMPUTERS():New()
    ::oWStoComputerUnit  := ComputerUnit_COMPUTERS():New()
Return

WSMETHOD RESET WSCLIENT WSComputerUnit
    ::nComputerValue     := NIL
    ::oWSfromComputerUnit := NIL
    ::oWStoComputerUnit  := NIL
    ::nChangeComputerUnitResult := NIL
    ::Init()
Return

WSMETHOD CLONE WSCLIENT WSComputerUnit
Local oClone := WSComputerUnit():New()
    oClone:_URL          := ::_URL
    oClone:nComputerValue := ::nComputerValue
    oClone:oWSfromComputerUnit :=  IIF(::oWSfromComputerUnit = NIL , NIL ,::oWSfromComputerUnit:Clone() )
    oClone:oWStoComputerUnit :=  IIF(::oWStoComputerUnit = NIL , NIL ,::oWStoComputerUnit:Clone() )
    oClone:nChangeComputerUnitResult := ::nChangeComputerUnitResult
Return oClone

// WSDL Method ChangeComputerUnit of Service WSComputerUnit

WSMETHOD ChangeComputerUnit WSSEND nComputerValue,oWSfromComputerUnit,oWStoComputerUnit WSRECEIVE nChangeComputerUnitResult WSCLIENT WSComputerUnit
Local cSoap := "" , oXmlRet

BEGIN WSMETHOD

cSoap += '<ChangeComputerUnit xmlns="http://www.webserviceX.NET/">'
cSoap += WSSoapValue("ComputerValue", ::nComputerValue, nComputerValue , "double", .T. , .F., 0 , NIL, .F.)
cSoap += WSSoapValue("fromComputerUnit", ::oWSfromComputerUnit, oWSfromComputerUnit , "Computers", .T. , .F., 0 , NIL, .F.)
cSoap += WSSoapValue("toComputerUnit", ::oWStoComputerUnit, oWStoComputerUnit , "Computers", .T. , .F., 0 , NIL, .F.)
cSoap += "ChangeComputerUnit>"

oXmlRet := SvcSoapCall(    Self,cSoap,;
    "http://www.webserviceX.NET/ChangeComputerUnit",;
    "DOCUMENT","http://www.webserviceX.NET/",,,;
    "http://www.webservicex.net/ConvertComputer.asmx")

::Init()
::nChangeComputerUnitResult :=  WSAdvValue( oXmlRet,"_CHANGECOMPUTERUNITRESPONSE:_CHANGECOMPUTERUNITRESULT:TEXT","double",NIL,NIL,NIL,NIL,NIL,NIL)

END WSMETHOD

oXmlRet := NIL
Return .T.


// WSDL Data Enumeration Computers

WSSTRUCT ComputerUnit_Computers
    WSDATA   Value                     AS string
    WSDATA   cValueType                AS string
    WSDATA   aValueList                AS Array Of string
    WSMETHOD NEW
    WSMETHOD CLONE
    WSMETHOD SOAPSEND
    WSMETHOD SOAPRECV
ENDWSSTRUCT

WSMETHOD NEW WSCLIENT ComputerUnit_Computers
    ::Value := NIL
    ::cValueType := "string"
    ::aValueList := {}
    aadd(::aValueList , "Bit" )
    aadd(::aValueList , "Byte" )
    aadd(::aValueList , "Kilobyte" )
    aadd(::aValueList , "Megabyte" )
    aadd(::aValueList , "Gigabyte" )
    aadd(::aValueList , "Terabyte" )
    aadd(::aValueList , "Petabyte" )
Return Self

WSMETHOD SOAPSEND WSCLIENT ComputerUnit_Computers
    Local cSoap := ""
    cSoap += WSSoapValue("Value", ::Value, NIL , "string", .F. , .F., 3 , NIL, .F.)
Return cSoap

WSMETHOD SOAPRECV WSSEND oResponse WSCLIENT ComputerUnit_Computers
    ::Value := NIL
    If oResponse = NIL ; Return ; Endif
    ::Value :=  oResponse:TEXT
Return

WSMETHOD CLONE WSCLIENT ComputerUnit_Computers
Local oClone := ComputerUnit_Computers():New()
    oClone:Value := ::Value
Return oClone


Salve o arquivo com o nome que desejar, e compile este fonte.
Veja agora no exemplo abaixo como consumir este WS:


#include "protheus.ch"
User Function FUConsumirWS()
    // Criando o objeto Web Service
    _oWSTeste := WSComputerUnit():New()
    // A Variavel NCOMPUTERVALUE é o numero que deseja converter
    _oWSTeste:NCOMPUTERVALUE := 1000   
    // A Variavel _oWSTeste:oWSFROMCOMPUTERUNIT:VALUE é de qual medida deseja 
    // converter
    _oWSTeste:oWSFROMCOMPUTERUNIT:VALUE := "Gigabyte"                            
    // A Variavel _oWSTeste:oWSFROMCOMPUTERUNIT:VALUE é para qual medida deseja
    // converter
    _oWSTeste:oWSTOCOMPUTERUNIT:VALUE        := "Kilobyte"
    // Executa o metodo ChangeComputerUnit
    _oWSTeste:ChangeComputerUnit()       
    // Mostra o resultado
    MsgStop(_oWSTeste:NCHANGECOMPUTERUNITRESULT)
Return

Estas variáveis que utilizo na função acima foram criadas pelo client do WS, todos os clients gerados pelo Protheus seguem esse modelo, portanto voce poderá consumir o WS que desejar.

Qualquer duvida deixe seu recado.

Um abraço

segunda-feira, 16 de agosto de 2010

Como usar a função StartJob

Dias atrás tive o seguinte problema: Precisava executar uma rotina dentro de uma função mas essa primeira rotina não poderia parar e aguardar a execução desta função. Ai conheci a função StartJob. Está função permite iniciar uma Thread, ou seja, uma conexão separada no monitor, e a função que à chamou continuar rodando normalmente.

Sintaxe da função StartJob:

StartJob(NomeDaRotina,Environment,FinalizaRotina,Empresa,Filial)
Esta sintaxe é o que o TOTVS disponibiliza.
Até ai tudo bem, mas eu tive mais um problema: Eu precisava passar parametros para esta rotina que eu estava chamando na função StartJob. Fiz vários testes até que descobri que ao invés de passar os parametros Empresa e Filial, eu poderia passar um vetor com os parametros, ou seja, no mesmo vetor poderia passar Empresa, Filial e mais os parametros que precisava. E para receber estes parametros na função destino bastou usar o PARAMIXB. Abaixo segue um exemplo, apenas escrevi este exemplo, nao compilei.

// Funcao principal que dentro dela voce chamará um Job
User Function FUAtualizaDados()
   Local _aParametros := {}
   _aParametros := {"01","02","000001","04"}
   // Inicia o Job
   StartJob("U_FUTesteJob()",GetEnvServer(),.F.,_aParametros)
   /* A Função GetEnvServer() retorna o environment atual, mas posso definir outro. */
Return

// Esta funcao e a funcao chamada pela funcao StartJob
User Function FUTesteJob()
   Local _cEmpresa := paramixb[1] // Usar o paramixb
   Local _cFilial  := paramixb[2]
   Local _cFornece := paramixb[3]
   Local _cLoja    := paramixb[4]
   /* A maioria das funcoes que sao chamadas de Job devem usar o prepare environment */
   prepare environment _cEmpresa Filial _cFilial Tables "SA2"
   DBSelectArea("SA2")
   SA2->(DBSetOrder(1))
   if SA2->(DBSeek(xFilial("SA2")+_cFornece+_cLoja))
      RecLock("SA2",.F.)
      SA2->A2_TESTE := "TESTANDO FUNCAO JOB"
      SA2->(MsUnLock())
   endif
Return

Pessoal lembre-se que ao iniciar um Job voce estara usando uma licenca e vc pode ter problemas com isso. Daqui uns dias eu posto uma maneira de voce criar jobs sem usar licenças.

Qualquer duvida sobre o conteudo acima é só deixar seu recado.

Um abraço...

domingo, 15 de agosto de 2010

Compilando o Protheus sem precisar parar os serviços

Olá pessoal, não sei se isso é novidade pra vocês mas achei interessante postar: Muitas vezes precisamos compilar o Protheus e nos deparamos com 20, 30, 100 usuários conectados. Vou passar como podemos compilar o Protheus sem precisar fazer com que os usuários saiam do sistema.

  • Neste exemplo as minhas pastas do Protheus de Produção (Chamo de Produção pois não é o Ambiente ou Protheus de testes) estão da seguinte forma: C:\Protheus10\apo, C:\Protheus10\system.
  • Dentro da pasta que esta o RPO (C:\Protheus10\apo) crie as seguintes pastas: APO01, APO02, APO03.
  • Copie o RPO original para dentro de cada pasta criadas acima. 
  • Crie um server separado somente para compilar, o mesmo não precisa estar como serviço e sim em console. Este serviço vamos chamar de Servico de Compilação.
  • No IDE ou Development Studio deste Serviço de Compilação, você irá criar 03 Configurações de Compilação
Vá em menu Arquivo - Configurações - 
Adicionar 
Descrição: APO01 
Ambiente: APO01 
Conexão: TCP (ou aquela que você definiu no .ini do SmartClient (Remote) 
Diretório de Include: Local aonde se encontram as suas includes 
  • Faca o mesmo procedimento acima para os outros dois APOs, (APO02, APO03) 
  • Após isso abra o .ini do Server de Compilação 
  • Crie os ambientes usando o exemplo abaixo: 
;Esta seção é do ambiente APO01 - para compilação 
[APO01] 
SourcePath=C:\Protheus10\apo\APO01 
RootPath=C:\Protheus10\ 
StartPath=\system\ 
RpoDb=dbf 
RpoLanguage=portuguese 
RpoVersion=101 
LocalFiles=ads 
Trace=0 
localdbextension=.dbf 
PictFormat=DEFAULT 
DateFormat=DEFAULT 

;Esta seção é do ambiente APO02 - para compilação 
[APO02] 
SourcePath=C:\Protheus10\apo\APO02 
RootPath=C:\Protheus10\ 
StartPath=\system\ 
RpoDb=dbf 
RpoLanguage=portuguese 
RpoVersion=101 
LocalFiles=ads 
Trace=0 
localdbextension=.dbf 
PictFormat=DEFAULT 
DateFormat=DEFAULT 

;Esta é a seção do ambiente APO03 - para compilação 
[APO03] 
SourcePath=C:\Protheus10\apo\APO03 
RootPath=C:\Protheus10\ 
StartPath=\system\ 
RpoDb=dbf 
RpoLanguage=portuguese 
RpoVersion=101 
LocalFiles=ads 
Trace=0 
localdbextension=.dbf 
PictFormat=DEFAULT 
DateFormat=DEFAULT 

  • Depois de criada as seções de compilação no .ini do Server de Compilação, você poderá compilar os fontes usando qualquer um dos ambientes criados (APO01, APO02, APO03). Use por exemplo o APO02
  • Agora é a vez de alteramos o caminho do RPO (SourcePath) que o Protheus de Produção esta configurado. Como compilamos no APO02, o caminho será: 
;Eu estou dizendo aqui que meu environment principal chama-se
;PRODUCAO, no caso de vocês provavelmente é outro nome 
[PRODUCAO] 
SourcePath=C:\Protheus10\apo\APO02 
RootPath=C:\Protheus10\ 
StartPath=\system\ 
RpoDb=dbf 
RpoLanguage=portuguese 
RpoVersion=101 
LocalFiles=ads 
Trace=0 
localdbextension=.dbf 
PictFormat=DEFAULT 
DateFormat=DEFAULT 

  • Veja acima como ficou o SourcePath (C:\Protheus10\apo\APO02), então significa que agora o Protheus de Produção esta sendo apontado para outro rpo; 
  • Salve e feche o .ini do Protheus de Produção. 
  • Agora quem acessar o sistema estará direcionado para o APO02, e quem esta no sistema, aparecera uma mensagem para o usuário dizendo que o RPO foi atualizado.
  • Na próxima compilação você usará o APO03, e depois o APO01, assim por diante. 
Se vocês possuem uma base teste, poderão fazer isso em teste antes de colocar em produção.


Pessoal qualquer dúvida deixe seu comentário.


Um abraço