Oracle
 sql >> Datenbank >  >> RDS >> Oracle

Wie verwende ich Oracle DBMS_ALERT in Oracle APEX?

Ich würde eine Demo auf apex.oracle.com einrichten, aber da Sie eine Ausführungserlaubnis für dbms_alert benötigen, muss es nur Text sein.

Sie können mit dem gesamten Setup ziemlich weit gehen, daher würde ich dies als Grundlagen betrachten, auf denen Sie aufbauen können. Zum Beispiel habe ich nur mit einer Warnung gearbeitet. In Ihrem Beispiel möchten Sie möglicherweise mehrere Ereignisse verwenden, um die verschiedenen Fortschrittswarnungen abzufangen. Dies hat den einfachen Grund, dass der Ajax-Callback geschlossen werden muss, um etwas an den Client zurückzugeben (die Ajax-Antwort). Wenn Sie also eine Warnung abfangen und diese zurückgeben möchten, müssen Sie in den Puffer schreiben, und er muss zurückgegeben werden. Dies bedeutet, dass Sie auch aufhören, das Ereignis zu hören (lesen Sie:in Apex sollten Sie!).

Stellen Sie sich den Ablauf wie folgt vor:Sie tätigen einen Ajax-Anruf und haben einen Ajax-Rückrufprozess, der Interesse an einem Ereignis registriert. Dann warten Sie, bis eine Warnung auftritt. Sie fangen es ab und geben es zurück, indem Sie es in den http-Puffer schreiben (htp.p ). Das ist das Ende des Codes und Apex löscht den Puffer, der Ajax-Aufruf nimmt dann die Antwort auf und Sie können diese Rückgabe verwalten.
Vergessen Sie jedoch nicht:Apex verwendet Verbindungspooling und Datenbank Sessions werden nicht direkt verknüpft, sondern ständig wiederverwendet. Sie möchten eine Datenbanksitzung nicht „verschmutzt“ verlassen. Sie müssen Ihr Benachrichtigungsinteresse auch abmelden. Dies spricht auch für die Verwendung eindeutiger IDs für Warnungen - Warnungen können in verschiedenen (Datenbank-) Sitzungen registriert werden. Wenn dies also eine Seite wäre, die mehrere Benutzer verwenden können, um den Fortschritt ihres Prozesses zu verfolgen, tun Sie dies nicht möchten, dass sie die Warnungen anderer Benutzer stören.

Diese flüchtige Art des Interesses bedeutet jedoch auch, dass es zwischen verschiedenen Ajax-Aufrufen zu "Unterbrechungen" kommt. Wenn Sie auf mehrere Warnungen hören möchten und diese Warnungen möglicherweise sehr, sehr eng beieinander liegen, besteht die Möglichkeit, dass Sie eine übersehen. Angenommen, 2 Warnungen sind 1 ms voneinander entfernt:Die erste wird abgefangen und an den Ajax-Aufruf gemeldet, der sofort einen neuen Anruf starten müsste, um auf weitere Warnungen zu warten. Aber da es während dieser kurzen Zeit keinen aktiven Zuhörer gab, wurde dieser nächste Alarm möglicherweise verpasst. Nun - dies ist wahrscheinlich nur ein Problem, wenn Sie mehrere Warnungen unter demselben Handler auslösen. Wenn Sie mehrere Handler verwenden und für alle gleichzeitig Ajax-Aufrufe starten, werden sie alle rechtzeitig verarbeitet. Für beides gibt es natürlich Lösungen. Ich stelle mir vor, dass Sie bei Verwendung nur eines Handlers alle Warnungen in einer Sammlung abfangen und damit prüfen könnten, ob Sie bereits eine Antwort für eine bestimmte Warnung gesendet haben oder nicht, und ob Sie mit dem Einchecken fortfahren möchten oder nicht. Bei mehreren Handlern könnten Sie eine eindeutige ID verwenden und ihr unterschiedliche Status anhängen.

Hier ist also tatsächlicher Code, den ich in meinem lokalen POC verwendet habe.

Übersicht:Ich habe 3 Schaltflächen:1 zum Generieren einer Alarm-ID, für die ich eine Sequenz verwendet habe. Eine weitere Schaltfläche, um mit der Überwachung eines Ereignisses zu beginnen, und eine weitere Schaltfläche, um eine Warnung zu senden.

JS-Code für die NEW_ALERT_ID-Schaltfläche:

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

JS-Code für die Schaltfläche 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?
    }
});

JS-Code für die SEND_ALERT-Schaltfläche:

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

AJAX-Callback-Prozesse:

NEW_ALERT:

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;

Also würde ich zuerst eine Alarm-ID bekommen, dann würde ich anfangen zu lauschen, und dann würde ich irgendwann einen Alarm senden (oder auch nicht). Es ist jedoch ein Skelett und muss in Ihrem tatsächlichen Setup weiter verfeinert werden.