Stworzyłbym demo na apex.oracle.com, ale ponieważ potrzebujesz grantu na wykonanie na dbms_alert, będzie to tylko tekstowe.
Możesz posunąć się dość daleko z całą konfiguracją, więc uważam to za podstawę do budowania. Na przykład pracowałem tylko z jednym alertem. W swojej próbce możesz chcieć użyć wielu zdarzeń, aby wychwycić różne alerty postępu. Dzieje się tak z tego prostego powodu, że aby zwrócić coś klientowi (odpowiedź ajax), wywołanie zwrotne ajax musi zostać 'zamknięte'. Więc kiedy złapiesz alert i chcesz go zwrócić, musisz napisać do bufora i musi zostać zwrócony. Oznacza to, że przestaniesz też słuchać wydarzenia (czytaj:w apesie, powinieneś!).
Rozważ przepływ w ten sposób:wykonasz wywołanie ajax i będziesz miał proces wywołania zwrotnego ajax, który rejestruje zainteresowanie zdarzeniem. Następnie czekasz na pojawienie się alertu. Łapiesz go i zwracasz, zapisując go w buforze http (htp.p
). To koniec kodu i apex opróżni bufor, a następnie wywołanie ajax odbierze odpowiedź i będziesz mógł zarządzać tym zwrotem.
Nie zapominaj jednak:apex używa puli połączeń i bazy danych sesje nie są połączone bezpośrednio, ale są ponownie wykorzystywane przez cały czas. Nie chcesz „pozostawiać” sesji bazy danych „zabrudzonej”. Będziesz musiał również wyrejestrować swoje zainteresowanie alertem. Stwarza to również powód do używania unikalnych identyfikatorów dla alertów – alerty mogą być rejestrowane w różnych sesjach (bazy danych), więc jeśli byłaby to strona, z której wielu użytkowników może korzystać do śledzenia postępu ich procesu, nie chcesz, aby zakłócały alerty innych użytkowników.
Jednak ten ulotny charakter zainteresowania oznacza również, że będą „przerwy” między różnymi wykonanymi połączeniami ajaxowymi. Jeśli chcesz wysłuchać wielu alertów, a alerty te mogą być bardzo blisko siebie upakowane, istnieje szansa, że jeden z nich przegapisz. Powiedzmy, że 2 alerty są oddalone od siebie o 1 ms:pierwszy zostanie przechwycony, zgłoszony do połączenia ajax, które będzie musiało od razu rozpocząć nowe połączenie, aby nasłuchiwać kolejnych alertów. Ale ponieważ w tym krótkim czasie nie było aktywnego słuchacza, następny alert mógł zostać pominięty. Teraz – prawdopodobnie jest to problem polegający na tym, że uruchamiasz wiele alertów w ramach tego samego modułu obsługi. Jeśli użyjesz wielu programów obsługi i uruchomisz wywołania ajax dla wszystkich w tym samym czasie, wszystkie zostaną obsłużone na czas. Oczywiście istnieją rozwiązania dla obu. Wyobrażam sobie, że korzystając tylko z jednego programu obsługi, można przechwycić wszystkie alerty w kolekcji i sprawdzić je, jeśli już wysłałeś odpowiedź na określony alert, czy nie, i czy kontynuować sprawdzanie, czy nie. W przypadku wielu programów obsługi możesz użyć unikalnego identyfikatora i dodać do niego różne statusy.
Oto rzeczywisty kod, którego użyłem w moim lokalnym POC.
Przegląd:Mam 3 przyciski:1 do generowania identyfikatora alertu, dla którego użyłem sekwencji. Kolejny przycisk do rozpoczęcia nasłuchiwania zdarzenia i kolejny przycisk do wysyłania alertu.
Kod JS przycisku NEW_ALERT_ID:
apex.server.process("NEW_ALERT").done(function(pdata){
$s("P1_ALERT_ID",pdata.alertId);
})
Kod JS przycisku 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?
}
});
Kod JS przycisku SEND_ALERT:
apex.server.process("SEND_ALERT",{x01:$v("P1_ALERT_ID")},{dataType:"text"});
Procesy wywołania zwrotnego AJAX:
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;
Więc najpierw otrzymywałem identyfikator alertu, potem zacząłem słuchać, a potem w pewnym momencie wysłałem alert (lub nie). Jest to jednak szkielet i będzie wymagał dalszego dopracowania w twojej aktualnej konfiguracji.