How to use Oracle DBMS_ALERT within Oracle APEX?

烈酒焚心 提交于 2019-12-11 04:28:41

问题


Using Oracle APEX 4.2, I have a page process that uses DBMS_ALERT for a running process that may take up to a minute to complete or fail.

While this is running, through the use of a trigger, there is a DBMS_ALERT.signal posting notifications of progress.

My question is, within Oracle APEX 4.2, how can I display to the user, a running progress of these notifications from DBMS_ALERT, within a region or modal on the page?

I need to provide the user all these alerts as the job is running until it finally completes.

For example:

Started Job...
Waiting on resources...
Processing job...
Job completed successfully.

Obviously through each of these DBMS_ALERTS, there may be a few seconds before each line is presented to the user.

I am unsure how to grab these alerts and show the user, within Oracle APEX.


回答1:


I'd set up a demo on apex.oracle.com, but since you need an execute grant on dbms_alert, it'll have to be textual-only.

You can go quite far with the entire set-up, so I'd consider this as basics to build on. For example, I have only worked with one alert. In your sample you may want to use multiple events to catch the different progress alerts. This is for the simple reason that in order to return something to the client (the ajax response) the ajax callback has to 'closed'. So when catching an alert and wanting to return that, you need to write to the buffer and it needs to be returned. This means that you will also stop listening to the event (read: in apex, you should!).

Consider the flow like this: you will make an ajax call, and have an ajax callback process which registers interest in an event. Then you wait for an alert to occur. You catch it and return it by writing it in the http buffer (htp.p). That's the end of the code and apex will flush the buffer, the ajax call will then pick up the response and you'll be able to manage that return.
Don't forget though: apex uses connection pooling, and database sessions are not linked directly but rather reused all the time. You don't want to 'leave' a database session 'dirtied'. You'll have to unregister your alert-interest too. This also makes a case for using unique IDs for alerts - alerts can be registered to in different (database) sessions, so if this would be a page which multiple users can use to follow the progress of their process' progress, you don't want them to interfere with other users' alerts.

However, this fleeting nature of interest also means there will be "interruptions" between different ajax calls made. When you want to listen for multiple alerts, and these alerts may be packed very very closely together, the chance exists you may miss one. Say 2 alerts are spaced 1ms apart: the first will be caught, reported to the ajax call, which would have to start a new call right away in order to listen for more alerts. But since there was no active listener during that short time, that next alert may have been missed. Now - this is likely only to be an issue where you fire off multiple alerts under the same handler. If you'd use multiple handlers and start ajax calls for all those at the same time, they'll all be handled in time. There are solutions for both, of course. I imagine that when using one handler only you could catch all alerts in a collection and check against that if you've already sent a response for a certain alert or not and whether to continue checking in or not. With multiple handlers you could use a unique id and suffix it with different statusses.

So here is some actual code which I've used in my local POC.

Overview: I have 3 buttons: 1 to generate an alert id, for which I used a sequence. Another button to start listening for an event, and yet another button to send an alert.

JS code for NEW_ALERT_ID button:

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

JS code for START_LISTEN button:

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 for SEND_ALERT button:

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

AJAX Callback processes:

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;

So, I'd first get an alert ID, then I'd start to listen, and then at some point I'd send an alert (or not). It's a skeleton though and will need further refinement in your actual setup.



来源:https://stackoverflow.com/questions/42825441/how-to-use-oracle-dbms-alert-within-oracle-apex

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!