Este é o site histórico da documentação 4D. As documentações estão sendo movidas progressivamente para developer.4d.com

  •  
 
4D v20 R10
Serviços baseados nos procedimentos armazenados (exemplo)

Serviços baseados nos procedimentos armazenados (exemplo)  


 

 

No exemplo da seção Importação baseada nos procedimentos armazenados (exemplo), um procedimento armazenado se inicia e termina cada vez que se solicita uma operação de importação de dados. Neste exemplo, um procedimento armazenado é iniciado automaticamente quando inicia a base do servidor e pode ser iniciado e parado a vontade por qualquer 4D conectado a base. Logo como se executa, o procedimento armazenado pode responder de maneira assíncrona a múltiplos pedidos enviados pelos clientes conectados a base.

Enquanto a seção Importação baseada nos procedimentos armazenados (exemplo), mostra como otimizar um serviço 4D Server existente, este exemplo mostra como implementar serviços novos e personalizados disponíveis para todos as máquinas 4D client conectados. Além disso, este exemplo pode ser utilizado como modelo para a criação de seus próprios serviços.

O procedimento armazenado é lançado automaticamente pelo Método banco de dados On Server Startup:

 
  ` Método de base On Server Startup
 START SP SERVICES

Como o Método banco de dados On Server Startup lança ao método de projeto SP SERVICES como um procedimento armazenado, SP SERVICES começa a correr logo como a base se abre com 4D Server, tendo ou não clientes conectados à base. Na seguinte imagem, a janela de administração de 4D Server mostra o procedimento armazenado em execução quando nenhum cliente está conectado.

Este é o método de projeto START SP SERVICES:

  ` START SP SERVICES Project Method
 ◊vlSPServices:=Execute on server("SP SERVICES";32*1024;"SP SERVICES";*)

Como o comando Execute on server atua como New process ao ser chamado na máquina servidor, o mesmo método (START SP SERVICES) pode ser utilizado na máquina servidor ou em uma máquina cliente para iniciar a vontade o método SP SERVICES como um procedimento armazenado na máquina servidor.

O método de projeto STOP SP SERVICES “ordena” parar ao método de projeto SP SERVICES.

  ` Método de projeto STOP SP SERVICES
 SET PROCESS VARIABLE(◊vlSPServices;vbStopSPServices;True)

Quando é iniciado o método de projeto SP SERVICES, da a variável processo vbStopSPServices o valor False e depois executa um laço até que esta variável Booleana se transforme em True. O comando , permite a todo processo usuário executado no servidor ou em uma máquina cliente modificar o valor da variável vbStopSPServices, e portanto parar o procedimento armazenado a vontade.

O procedimento armazenado deve poder receber pedidos de clientes e responder de maneira assíncrona em qualquer momento e sem importar a ordem. Uma maneira direta de assegurar esta comunicação é utilizar uma tabela.

A tabela [SP Requests] contém os seguintes campos:

  • [SP Requests]reqID é assinalado utilizando o comando Sequence number. Este campo identifica cada pedido de maneira única.
  • [SP Requests]reqType descreve o tipo do pedido.
  • [SP Requests]reqStatus pode tomar um dos seguintes valores:

ValorDescrição
1foi publicada a petição mas não foi processada.
0foi processado com êxito a petição.
< 0foi processada a petição mas ocorreu um erro.

Nota: Estes valores são escolhidos arbitrariamente para este exemplo, não são impostos por 4D.

  • [SP Requests]reqData é um BLOB que contém os dados da petição. Pode conter os dados enviados pela pessoa que solicita os dados devolvidos pelo procedimento armazenado ao requerente.
  • [SP Requests]reqParams contém eventualmente os valores dos parâmetros enviados pelo requerente ao procedimento armazenado.

A comunicação entre um processo cliente e um procedimento armazenado pode ser implantado utilizando os comandos GET PROCESS VARIABLE, SET PROCESS VARIABLE e VARIABLE TO VARIABLE. Por exemplo, esta solução utilizada na seção Importação baseada nos procedimentos armazenados (exemplo), como também no método de projeto STOP SP SERVICES listado anteriormente.

Aqui, o sistema deve permitir ao procedimento armazenado receber e reenviar as quantidades das variáveis de dados. Pode utilizar arrays, incluindo arrays de texto e imagem), mas há duas razões principais para preferir o emprego de uma tabela:

  • O algoritmo de gestão de petições sob os registros é mais fácil de implementar. Enviar uma petição desde uma máquina cliente consiste simplesmente em adicionar uma petição na tabela. Responder a petição desde o procedimento armazenado consiste simplesmente em modificar esta petição.
  • Como as petições se armazenam em uma tabela, são guardadas no disco. Portanto, se o tamanho de uma petição é importante, não será um problema na medida em que possa ser suprimido da memória (a diferença dos dados armazenados nos arrays).

O método de projeto Client post request é um método genérico para enviar uma petição:

  ` Método de projeto Client post request
  ` Client post request ( Cadeia { ; Texto } ) -> Inteiro longo
  ` Client post request ( Tipo de petição { ; Parâmetros } ) -> Número de petição
 CREATE RECORD([SP Requests])
 [SP Requests]reqID:=Sequence number([SP Requests])
 [SP Requests]reqType:=$1
 [SP Requests]reqStatus:=1
 If(Count parameters>=2)
    [SP Requests]reqParams:=$2
 End if
 SAVE RECORD([SP Requests])
 $0:=[SP Requests]reqID

O método devolve o número da petição, cuja unicidade está garantida pelo uso do comando Sequence number. Uma vez adicionado o registro a tabela [SP Requests], o cliente não têm que interrogar regularmente o campo [SP Requets]redStatus até que o procedimento armazenado termine de processar a petição.

O método de projeto Client get result é um método genérico para provar o estado da petição. Como foi explicado anteriormente, logo como o campo [SP Requets]redStatus se torna diferente de 1, o cliente sabe que o procedimento armazenado foi tratado (com êxito ou não) a petição.

  ` Método de projeto Client get result
  ` Client get result ( Inteiro longo ; ->BLOB {; Inteiro longo } ) -> Inteiro longo
  ` Client get result ( Número de petição ; ->Dados {; Duração } ) -> Código do erro
 C_LONGINT($0;$1;$vlDelay)
 $0:=1
 $vlDelay:=0
 If(Count parameters>=3)
    $vlDelay:=$3
 End if
 READ ONLY([SP Requests])
 Repeat
    QUERY([SP Requests];[SP Requests]reqID=$1)
    If(Records in selection([SP Requests])>0)
       If([SP Requests]reqStatus&NBSP;#&NBSP;1)
          $2->:=[SP Requests]reqData
          READ WRITE([SP Requests])
          While(Locked([SP Requests]))
             WAITING LOOP($vlDelay)
             LOAD RECORD([SP Requests])
          End while
          DELETE RECORD([SP Requests])
          $0:=[SP Requests]reqStatus
       End if
    Else
  ` Se perdeu o registro da petição
  ` Isto não deveria passar. Mas de todas formas estabelece o código de erro -2 (valor arbitrário)
       $0:=-2
    End if
  ` A petição ainda não foi processada
    If($0=1)
       WAITING LOOP($vlDelay)
    End if
 Until($0&NBSP;#&NBSP;1)
 READ ONLY([SP Requests])

Se a petição foi administrada com êxito pelo procedimento armazenado, o método copia o resultado (se existe) do registro ao BLOB cujo ponteiro foi passado como parâmetro. O método chamado depois analisa e utiliza os dados do BLOB em função do tipo de pedido. Note que o cliente está encarregado de apagar o registro [SP Requests] uma vez termina o pedido.

O pequeno método de projeto WAITING LOOP faz um laço até um certo número de tics:

  ` Método de projeto WAITING LOOP
  ` WAITING LOOP ( Inteiro longo )
  ` WAITING LOOP ( Tempo em tics )
 C_LONGINT($1)
 $vlStartTicks:=Tickcount
 Repeat
    IDLE
 Until((Tickcount-$vlStartTicks)>=$1)

Lembrança: DELAY PROCESS não tem efeito no processo principal. Se utiliza o método de projeto WAITING LOOP, o processo esperará a quantidade de tempo necessário, ainda que o pedido tenha origem desde o processo do ambiente usuário de uma máquina cliente.

O método de projeto SP SERVICES é o método executado como procedimento armazenado na máquina servidor. A estrutura geral deste método, a continuação,  muito simples:

   Inicialização de uma variável “stop”
   Repetir
      Pesquisa das petições cujo campo [SP Requests]reqStatus é igual a 1
      Para cada petição
         Em função do tipo de petição, chamar uma subrotina
            que guarde o resultado no campo [SP Requests]reqData
         Mudar o estado da petição para que o cliente saiba que passou
      End for
      “Dormir” um pouco antes de voltar a começar
   Até que a variável “stop” se torne true

Este é o código fonte real:

  ` Método de projeto SP SERVICES
  ` O procedimento armazenado começa
 vbStopSPServices:=False
  ` O procedimento armazenado não necessita acesso em leitura escritura as tabelas...
 READ ONLY(*)
  ` ...exceto a tabela [SP Requests]
 READ WRITE([SP Requests])
 Repeat
  ` Pesquisa dos pedidos ainda não processados
    QUERY([SP Requests];[SP Requests]reqStatus=1)
  ` Processo destas petições uma por uma
    For($vlRecord;1;Records in selection([SP Requests]))
  ` Se o registro da petição está bloqueado, esperar até que esteja desbloqueado
       While(Locked([SP Requests]))
  ` Esperar um segundo antes de tentar novamente
          DELAY PROCESS(Current process;60)
  ` Trata de obter acesso leitura-escritura
          LOAD RECORD([SP Requests])
       End while
  ` Assume que a petição será processada com sucesso
       [SP Requests]reqStatus:=0
       Case of
          :([SP Requests]reqType="Server Information")
             SP DO SERVER INFORMATION
          :([SP Requests]reqType="Volume List")
             SP DO VOLUME LIST
          :([SP Requests]reqType="Browse Directory")
             SP DO BROWSE DIRECTORY([SP Requests]reqParams)
  ` ...
  ` OUTROS TIPOS DE PETIÇÕES PODERIAM SER INCLUÍDAS AQUI
  ` ...
          Else
  ` O tipo de petição é desconhecido, devolver o erro -1 (valor arbitrário)
             [SP Requests]reqStatus:=-1
       End case
  ` Forçar o estado da petição a outro diferente de 1
  ` (em caso de uma subrotina dar o valor 1)
       If([SP Requests]reqStatus=1)
          [SP Requests]reqStatus:=-3
       End if
  ` Atualizar o registro da petição
       SAVE RECORD([SP Requests])
  ` Ir a seguinte petição não processada
       NEXT RECORD([SP Requests])
    End for
  ` Liberar o último registro processado
    UNLOAD RECORD([SP Requests])
  ` Espere um segundo antes de continuar respondendo petições
    DELAY PROCESS(Current process;60)
  ` Bucle até que seja ordenado ao procedimento armazenado parar sua execução
 Until(vbStopSPServices)

O método de projeto SP SERVICES pode ser utilizado como modelo para implementar serviços inovadores em uma base. Nesta seção, detalhamos as subrotinas SP DO SERVER INFORMATION e SP DO VOLUME LIST. A subrotina SP DO BROWSE DIRECTORY (que toma como parâmetro um parâmetro enviado pelo cliente no campo [SP Requests]reqParams) não se trata neste documento.

Dependendo do tipo da petição, o método de projeto SP SERVICES chama a uma subrotina cuja tarefa é guardar os dados resultantes no campo [SP Requests]reqData. O armazenamento do registro e a mudança do estado a realizar o método de projeto SP SERVICES.

Esta é a subrotina SP DO SERVER INFORMATION que guarda a informação relativa ao servidor no BLOB. Outro método de projeto extrairá os dados do BLOB em função da máquina cliente.

  ` Método de projeto SP DO SERVER INFORMATION
 TEXT TO BLOB(Application version(*);[SP Requests]reqData;UTF8 C string)
 TEXT TO BLOB(Structure file;[SP Requests]reqData;UTF8 C string;*)
 TEXT TO BLOB(Data file;[SP Requests]reqData;UTF8 C string;*)
 PLATFORM PROPERTIES($vlPlatform;$vlSystem;$vlMachine)
 VARIABLE TO BLOB($vlPlatform;[SP Requests]reqData;*)
 VARIABLE TO BLOB($vlSystem;[SP Requests]reqData;*)
 VARIABLE TO BLOB($vlMachine;[SP Requests]reqData;*)

Esta é a subrotina SP DO VOLUME LIST, que guarda a informação relativa aos volumes no BLOB. Outro método de projeto extrairá os dados do BLOB em função da máquina cliente.

  ` Método de projeto SP DO VOLUME LIST
 VOLUME LIST($asVName)
 $vlSize:=Size of array($asVName)
 ARRAY REAL($arVSize;$vlSize)
 ARRAY REAL($arVUsedSpace;$vlSize)
 ARRAY REAL($arVFreeSpace;$vlSize)
 For($vlElem;1;$vlSize)
    VOLUME ATTRIBUTES($asVName{$vlElem};$arVSize{$vlElem};$arVUsedSpace{$vlElem}
    ;$arVFreeSpace{$vlELem})
 End for
 VARIABLE TO BLOB($asVName;[SP Requests]reqData)
 VARIABLE TO BLOB($arVSize;[SP Requests]reqData;*)
 VARIABLE TO BLOB($arVUsedSpace;[SP Requests]reqData;*)
 VARIABLE TO BLOB($arVFreeSpace;[SP Requests]reqData;*)

Com os métodos de projeto genéricos Client post request e Client get result, o método de projeto M_SERVER_INFORMATION mostra, na máquina cliente, a informação devolvida pelo procedimento armazenado. Este método pode estar associado a um comando de menu ou ser chamado, por exemplo, desde o método de objeto de um botão:

  ` M_SERVER_INFORMATION
 C_BLOB(vxData)
 C_LONGINT($vlReqID;$vlErrCode;$vlOffset)
  ` Envio do pedido
 $vlReqID:=Client post request("Server Information")
  ` Prova do estado do pedido e recepção do resultado
 $vlErrCode:=Client get result($vlReqID;->vxData;60)
  ` Caso o pedido termina com sucesso, é mostrado o resultado
 If($vlErrCode=0)
  ` Extração da informação resultante do BLOB
    $vlOffset:=0
    vsServerVersion:=BLOB to text(vxData;UTF8 C string;$vlOffset)
    vsStructureFile:=BLOB to text(vxData;UTF8 C string;$vlOffset)
    vsDataFile:=BLOB to text(vxData;UTF8 C string;$vlOffset)
    BLOB TO VARIABLE(vxData;$vlPlatform;$vlOffset)
    BLOB TO VARIABLE(vxData;$vlSystem;$vlOffset)
    BLOB TO VARIABLE(vxData;$vlMachine;$vlOffset)
  ` Análises das propriedades da plataforma
    vs4DPlatform:="Versão de 4D Server desconhecida"
    vsSystem:="Versão do sistema desconhecida"
    vsMachine:="Computador desconhecido"
  `...
  ` Este é o código (não listado) que analisa $vlSystem e $vlMachine
  ` ( ver o exemplo do comando PLATFORM PROPERTIES)
  ` ...
  ` Visualização da informação resultante
    DIALOG([SP Requests];"SERVER INFORMATION")
 Else
    ALERT("Erro de pedido "+String($vlErrCode))
 End if
  ` Não é mais necessário o BLOB
 CLEAR VARIABLE(vxData)

Este é o formulário [SP Requests];"SERVER INFORMATION" em execução:

Com os métodos de projeto genéricos Client post request e Client get result, o método de projeto M_SERVER_VOLUMES mostra, na máquina cliente, a informação na lista dos volumes do servidor devolvidos pelo procedimento armazenado. Este método pode estar associado a um comando de menu ou chamado, por exemplo, desde o método de objeto de um botão:

  ` M_SERVER_VOLUMES
 C_BLOB(vxData)
  ` Envio do pedido
 $vlReqID:=Client post request("Volume List")
  ` Prova do estado da petição e recepção do resultado
 $vlErrCode:=Client get result($vlReqID;->vxData;120)
  ` Se a petição termina com sucesso, mostra o resultado
 If($vlErrCode=0)
  ` Extração da informação resultante do BLOB
    $vlOffset:=0
    BLOB TO VARIABLE(vxData;asVName;$vlOffset)
    BLOB TO VARIABLE(vxData;arVSize;$vlOffset)
    BLOB TO VARIABLE(vxData;arVUsedSpace;$vlOffset)
    BLOB TO VARIABLE(vxData;arVFreeSpace;$vlOffset)
    For($vlElem;1;Size of array(arVSize))
  ` Conversão de bytes em MB
       arVSize{$vlElem}:=arVSize{$vlElem}/1048576
       arVUsedSpace{$vlElem}:=arVUsedSpace{$vlElem}/1048576
       arVFreeSpace{$vlElem}:=arVFreeSpace{$vlElem}/1048576
    End for
  ` Visualização da informação resultante
    DIALOG([SP Requests];"VOLUME LIST")
 Else
    ALERT("Erro de petição "+String($vlErrCode))
 End if
  ` Já não é necessário o BLOB
 CLEAR VARIABLE(vxData)

Este é o formulário [SP Requests];"VOLUME LIST" em execução:



参照 

Importação baseada nos procedimentos armazenados (exemplo)
Procedimentos armazenados