Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

Como usar o Oracle DBMS_ALERT no Oracle APEX?


Eu configuraria uma demonstração em apex.oracle.com, mas como você precisa de uma concessão de execução em dbms_alert, ela terá que ser somente textual.

Você pode ir muito longe com toda a configuração, então eu consideraria isso como o básico para construir. Por exemplo, eu trabalhei apenas com um alerta. Em sua amostra, você pode usar vários eventos para capturar os diferentes alertas de progresso. Isso é pelo simples motivo de que, para retornar algo ao cliente (a resposta do ajax), o retorno de chamada do ajax deve ser 'fechado'. Então, ao pegar um alerta e querer devolvê-lo, você precisa escrever no buffer e ele precisa ser retornado. Isso significa que você também deixará de ouvir o evento (leia-se:no apex, você deveria!).

Considere o fluxo assim:você fará uma chamada ajax e terá um processo de retorno de chamada ajax que registra interesse em um evento. Então você espera que um alerta ocorra. Você o pega e o retorna escrevendo-o no buffer http (htp.p ). Esse é o fim do código e o apex liberará o buffer, a chamada do ajax pegará a resposta e você poderá gerenciar esse retorno.
Não se esqueça:o apex usa pool de conexão e banco de dados as sessões não são vinculadas diretamente, mas sim reutilizadas o tempo todo. Você não quer 'deixar' uma sessão de banco de dados 'suja'. Você também terá que cancelar o registro de seu interesse de alerta. Isso também justifica o uso de IDs exclusivos para alertas - os alertas podem ser registrados em diferentes sessões (do banco de dados), portanto, se essa for uma página que vários usuários podem usar para acompanhar o andamento do andamento do processo, você não quer que eles interfiram nos alertas de outros usuários.

No entanto, essa natureza fugaz de interesse também significa que haverá "interrupções" entre diferentes chamadas ajax feitas. Quando você deseja ouvir vários alertas, e esses alertas podem estar muito próximos uns dos outros, existe a chance de você perder um. Digamos que 2 alertas estejam espaçados de 1ms:o primeiro será capturado, reportado à chamada ajax, que teria que iniciar uma nova chamada imediatamente para ouvir mais alertas. Mas como não houve ouvinte ativo durante esse curto período de tempo, o próximo alerta pode ter sido perdido. Agora - isso provavelmente será apenas um problema em que você dispara vários alertas no mesmo manipulador. Se você usar vários manipuladores e iniciar chamadas ajax para todos eles ao mesmo tempo, todos serão tratados a tempo. Há soluções para ambos, é claro. Imagino que, ao usar apenas um manipulador, você possa capturar todos os alertas em uma coleção e verificar se já enviou uma resposta para um determinado alerta ou não e se deve continuar o check-in ou não. Com vários manipuladores, você pode usar um id exclusivo e sufixo-o com status diferentes.

Então aqui está um código real que eu usei no meu POC local.

Visão geral:Tenho 3 botões:1 para gerar um id de alerta, para o qual usei uma sequência. Outro botão para começar a ouvir um evento e outro botão para enviar um alerta.

Código JS para o botão NEW_ALERT_ID:
apex.server.process("NEW_ALERT").done(function(pdata){
$s("P1_ALERT_ID",pdata.alertId);
})

Código JS para o botão START_LISTEN:
apex.server.process("LISTEN_ALERT",{x01:$v("P1_ALERT_ID")},{timeout:(31*1000)})
.done(function(pdata){
  if (pdata.success ){
      alert('Caught alert: ' + pdata.message);
  } else {
      alert("No alerts caught during wait on database. You may want to continue listening in...")
  }
})
.fail(function(jqXHR, textStatus){
    if(textStatus === 'timeout')
    {     
        alert('Call should have returned by now...'); 
        //do something. Try again perhaps?
    }
});

Código JS para o botão SEND_ALERT:
apex.server.process("SEND_ALERT",{x01:$v("P1_ALERT_ID")},{dataType:"text"});

Processos de retorno de chamada AJAX:

NEW_ALERT:
htp.p('{"alertId":'||alert_seq.nextval()||'}');

LISTE_ALERT:
declare
  alert_id number := apex_application.g_x01;
  msg varchar2(2000);
  stat pls_integer;
  keep_looping boolean := true;
  insurance binary_integer := 0; -- prevent an infinite loop

  onecycle binary_integer := 3; -- one cycle of waiting, in seconds
  maxcycles binary_integer := 10; -- in this session, the max amount of cycles to wait
begin
  dbms_alert.register(alert_id);

  while keep_looping
  loop
    insurance := insurance + 1;

    dbms_alert.waitone(alert_id, msg, stat, onecycle);
    if stat = 1 then
      apex_debug.message('timeout occured, going again');
    else
      apex_debug.message('alert: '||msg);
      keep_looping := false;
    end if;

    exit when insurance = maxcycles;    
  end loop;


  if keep_looping then
    -- we waited a really long time now. It may be a good idea to return this info to the client and let it start a new call
    htp.p('{"success":false,"message":"No alert during wait on database"}');
  else
    htp.p('{"success":true,"message":"'||msg||'"}');
  end if;
end;

SEND_ALERT:
declare
  alert_id number := apex_application.g_x01;
begin
  dbms_alert.signal(alert_id, 'alert sent at '||to_char(systimestamp, 'HH24:MI:SS FF6'));
end;

Então, eu primeiro obteria um ID de alerta, depois começaria a ouvir e, em algum momento, enviaria um alerta (ou não). No entanto, é um esqueleto e precisará de mais refinamento em sua configuração real.