apex.oracle.com でデモを設定したいのですが、dbms_alert での実行許可が必要なので、テキストのみにする必要があります。
セットアップ全体でかなりの作業を行うことができるので、これを構築するための基本と考えます。たとえば、私は 1 つのアラートだけを処理しました。サンプルでは、複数のイベントを使用して、さまざまな進捗アラートをキャッチすることができます。これは、クライアントに何か (ajax 応答) を返すために ajax コールバックを「閉じる」必要があるという単純な理由によるものです。したがって、アラートをキャッチしてそれを返したい場合は、バッファに書き込む必要があり、それを返す必要があります。これは、イベントのリッスンも停止することを意味します (読み取り:apex では、そうする必要があります!)。
次のようなフローを考えてみましょう:ajax 呼び出しを行い、イベントへの関心を登録する ajax コールバック プロセスを用意します。次に、アラートが発生するのを待ちます。それをキャッチして、http バッファ (htp.p
) に書き込んで返します。 )。これでコードが終了し、apex がバッファをフラッシュし、ajax 呼び出しが応答を受け取り、その戻り値を管理できるようになります。
忘れないでください:apex は接続プーリングとデータベースを使用します。セッションは直接リンクされるのではなく、常に再利用されます。データベース セッションを「ダーティ」のまま「放置」したくありません。アラートの関心も登録解除する必要があります。これは、アラートに一意の ID を使用する場合にも当てはまります。アラートは異なる (データベース) セッションに登録できるため、これが複数のユーザーがプロセスの進行状況を追跡するために使用できるページである場合は、他のユーザーのアラートを妨害したい。
ただし、この一時的な関心の性質は、行われた異なる ajax 呼び出しの間に「中断」があることも意味します。複数のアラートをリッスンする必要があり、これらのアラートが非常に密集している可能性がある場合、1 つを見逃す可能性があります。 2 つのアラートが 1 ミリ秒間隔で配置されているとします。最初のアラートがキャッチされ、ajax 呼び出しに報告されます。この呼び出しは、さらにアラートをリッスンするためにすぐに新しい呼び出しを開始する必要があります。しかし、その短い時間の間にアクティブなリスナーがなかったため、次のアラートが見逃された可能性があります。現在 - これは、同じハンドラーで複数のアラートを発生させた場合にのみ発生する問題である可能性があります。複数のハンドラーを使用し、それらすべてに対して同時に ajax 呼び出しを開始すると、それらはすべて時間内に処理されます。もちろん、両方の解決策があります。 1 つのハンドラーのみを使用すると、コレクション内のすべてのアラートをキャッチし、特定のアラートに対する応答を既に送信しているかどうか、およびチェックインを続行するかどうかを確認できると思います。複数のハンドラーを使用すると、一意の ID を使用して、さまざまなステータスのサフィックスを付けることができます。
ここに、ローカル POC で使用した実際のコードを示します。
概要:3 つのボタンがあります。1 つは、シーケンスを使用したアラート ID を生成するためのものです。イベントのリッスンを開始する別のボタンと、アラートを送信する別のボタンです。
NEW_ALERT_ID ボタンの JS コード:
apex.server.process("NEW_ALERT").done(function(pdata){
$s("P1_ALERT_ID",pdata.alertId);
})
START_LISTEN ボタンの JS コード:
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?
}
});
SEND_ALERT ボタンの JS コード:
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;
そのため、最初にアラート ID を取得し、次にリッスンを開始し、ある時点でアラートを送信します (または送信しません)。ただし、これはスケルトンであり、実際のセットアップでさらに改良する必要があります。