Бих настроил демонстрация на apex.oracle.com, но тъй като имате нужда от разрешение за изпълнение на dbms_alert, тя ще трябва да бъде само текстова.
Можете да стигнете доста далеч с цялата настройка, така че смятам това за основа, върху която да надграждам. Например, работил съм само с един сигнал. Във вашата извадка може да искате да използвате множество събития, за да уловите различните предупреждения за напредъка. Това е поради простата причина, че за да върне нещо на клиента (отговора на ajax), обратното извикване на ajax трябва да е „затворено“. Така че, когато хванете предупреждение и искате да го върнете, трябва да пишете в буфера и той трябва да бъде върнат. Това означава, че вие също ще спрете да слушате събитието (прочетете:в апекс, трябва!).
Помислете за потока по следния начин:ще направите ajax повикване и ще имате процес за обратно извикване на ajax, който регистрира интерес към събитие. След това изчаквате да се появи предупреждение. Хващате го и го връщате, като го записвате в http буфера (htp.p
). Това е краят на кода и apex ще изчисти буфера, след това извикването на ajax ще вземе отговора и вие ще можете да управлявате това връщане.
Не забравяйте обаче:apex използва групиране на връзки и база данни сесиите не са свързани директно, а по-скоро се използват повторно през цялото време. Не искате да „оставите“ сесия на база данни „замърсена“. Ще трябва да дерегистрирате и своя интерес към предупреждение. Това също дава основание за използване на уникални идентификатори за сигнали - сигналите могат да бъдат регистрирани в различни сесии (база данни), така че ако това е страница, която множество потребители могат да използват, за да следят напредъка на техния процес, вие не искат те да се намесват в предупрежденията на други потребители.
Въпреки това, този мимолетен характер на интерес също означава, че ще има "прекъсвания" между различните извършени ajax повиквания. Когато искате да слушате за множество сигнали и тези сигнали може да са опаковани много много близо един до друг, съществува шанс да пропуснете едно. Да кажем, че 2 предупреждения са отдалечени на 1ms едно от друго:първото ще бъде уловено, докладвано на повикването на ajax, което трябва да започне ново повикване веднага, за да изслуша повече предупреждения. Но тъй като през това кратко време нямаше активен слушател, следващият сигнал може да е пропуснат. Сега - това вероятно ще бъде проблем само когато задействате множество предупреждения под един и същ манипулатор. Ако използвате множество манипулатори и стартирате ajax извиквания за всички тях едновременно, всички те ще бъдат обработени навреме. Има решения и за двете, разбира се. Предполагам, че когато използвате само един манипулатор, бихте могли да уловите всички предупреждения в колекция и да проверите дали вече сте изпратили отговор за определено предупреждение или не и дали да продължите да проверявате или не. С множество манипулатори можете да използвате уникален идентификатор и да добавите към него различни статуси.
И така, ето някакъв действителен код, който използвах в моя локален POC.
Преглед:Имам 3 бутона:1 за генериране на идентификатор за предупреждение, за който използвах последователност. Друг бутон, за да започнете да слушате за събитие, и още един бутон, за да изпратите предупреждение.
JS код за бутона NEW_ALERT_ID:
apex.server.process("NEW_ALERT").done(function(pdata){
$s("P1_ALERT_ID",pdata.alertId);
})
JS код за бутона 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 код за бутона SEND_ALERT:
apex.server.process("SEND_ALERT",{x01:$v("P1_ALERT_ID")},{dataType:"text"});
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;
И така, първо ще получа идентификатор за предупреждение, след това ще започна да слушам и след това в даден момент ще изпратя предупреждение (или не). Това обаче е скелет и ще се нуждае от допълнително усъвършенстване в действителната ви настройка.