Oracle
 sql >> Database >  >> RDS >> Oracle

Come utilizzare Oracle DBMS_ALERT all'interno di Oracle APEX?

Vorrei impostare una demo su apex.oracle.com, ma dal momento che hai bisogno di una concessione di esecuzione su dbms_alert, dovrà essere solo testuale.

Puoi andare molto lontano con l'intera configurazione, quindi lo considero come base su cui costruire. Ad esempio, ho lavorato solo con un avviso. Nel tuo campione potresti voler utilizzare più eventi per catturare i diversi avvisi di avanzamento. Questo è per il semplice motivo che per restituire qualcosa al client (la risposta ajax) il callback ajax deve essere "chiuso". Quindi, quando si riceve un avviso e si desidera restituirlo, è necessario scrivere nel buffer e deve essere restituito. Ciò significa che smetterai anche di ascoltare l'evento (leggi:in apice, dovresti!).

Considera il flusso in questo modo:effettuerai una chiamata ajax e avrai un processo di richiamata ajax che registra l'interesse per un evento. Quindi attendi che si verifichi un avviso. Lo prendi e lo restituisci scrivendolo nel buffer http (htp.p ). Questa è la fine del codice e apex svuota il buffer, la chiamata ajax raccoglierà quindi la risposta e sarai in grado di gestire quel ritorno.
Non dimenticare però:apex usa il pool di connessioni e il database le sessioni non sono collegate direttamente ma piuttosto riutilizzate continuamente. Non vuoi "lasciare" una sessione di database "sporcata". Dovrai annullare anche la registrazione del tuo interesse per gli avvisi. Ciò rende anche opportuno l'utilizzo di ID univoci per gli avvisi:gli avvisi possono essere registrati in diverse sessioni (database), quindi se questa fosse una pagina che più utenti possono utilizzare per seguire l'avanzamento del loro processo, non lo fai vogliono che interferiscano con gli avvisi di altri utenti.

Tuttavia, questa natura fugace di interesse significa anche che ci saranno "interruzioni" tra le diverse chiamate ajax effettuate. Quando vuoi ascoltare più avvisi e questi avvisi possono essere raggruppati molto molto strettamente insieme, esiste la possibilità che potresti perderne uno. Supponiamo che 2 avvisi siano distanziati di 1 ms:il primo verrà catturato, segnalato alla chiamata ajax, che dovrebbe iniziare immediatamente una nuova chiamata per ascoltare più avvisi. Ma poiché non c'era nessun ascoltatore attivo durante quel breve periodo, il prossimo avviso potrebbe essere stato perso. Ora, è probabile che questo sia solo un problema in cui si attivano più avvisi sotto lo stesso gestore. Se utilizzi più gestori e avvii chiamate ajax per tutti contemporaneamente, verranno gestiti tutti in tempo. Ci sono soluzioni per entrambi, ovviamente. Immagino che quando si utilizza un solo gestore è possibile catturare tutti gli avvisi in una raccolta e verificare se hai già inviato una risposta per un determinato avviso o meno e se continuare il check-in o meno. Con più gestori è possibile utilizzare un ID univoco e un suffisso con stati diversi.

Quindi ecco del codice effettivo che ho usato nel mio POC locale.

Panoramica:ho 3 pulsanti:1 per generare un ID avviso, per il quale ho usato una sequenza. Un altro pulsante per iniziare ad ascoltare un evento e un altro pulsante per inviare un avviso.

Codice JS per il pulsante NEW_ALERT_ID:

apex.server.process("NEW_ALERT").done(function(pdata){
$s("P1_ALERT_ID",pdata.alertId);
})

Codice JS per il pulsante 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?
    }
});

Codice JS per il pulsante SEND_ALERT:

apex.server.process("SEND_ALERT",{x01:$v("P1_ALERT_ID")},{dataType:"text"});

Processi di richiamata AJAX:

NUOVO_AVVISO:

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

LISTEN_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;

Quindi, prima otterrei un ID avviso, quindi inizierei ad ascoltare e poi a un certo punto invierò un avviso (o meno). Tuttavia, è uno scheletro e avrà bisogno di ulteriori perfezionamenti nella tua configurazione effettiva.