Oracle
 sql >> Base de données >  >> RDS >> Oracle

Comment utiliser Oracle DBMS_ALERT dans Oracle APEX ?

J'aurais mis en place une démo sur apex.oracle.com, mais comme vous avez besoin d'une autorisation d'exécution sur dbms_alert, elle devra être textuelle uniquement.

Vous pouvez aller assez loin avec l'ensemble de la configuration, donc je considérerais cela comme une base sur laquelle s'appuyer. Par exemple, je n'ai travaillé qu'avec une seule alerte. Dans votre exemple, vous souhaiterez peut-être utiliser plusieurs événements pour intercepter les différentes alertes de progression. C'est pour la simple raison que pour renvoyer quelque chose au client (la réponse ajax), le rappel ajax doit être "fermé". Ainsi, lorsque vous recevez une alerte et que vous souhaitez la renvoyer, vous devez écrire dans le tampon et il doit être renvoyé. Cela signifie que vous arrêterez également d'écouter l'événement (lire :en apex, vous devriez !).

Considérez le flux comme ceci :vous allez effectuer un appel ajax et disposer d'un processus de rappel ajax qui enregistre l'intérêt pour un événement. Ensuite, vous attendez qu'une alerte se produise. Vous l'attrapez et le renvoyez en l'écrivant dans le tampon http (htp.p ). C'est la fin du code et apex videra le tampon, l'appel ajax récupérera alors la réponse et vous pourrez gérer ce retour.
N'oubliez pas cependant :apex utilise le regroupement de connexions et la base de données les sessions ne sont pas liées directement mais plutôt réutilisées tout le temps. Vous ne voulez pas "laisser" une session de base de données "sali". Vous devrez également désinscrire votre intérêt d'alerte. Cela justifie également l'utilisation d'identifiants uniques pour les alertes - les alertes peuvent être enregistrées dans différentes sessions (de base de données), donc s'il s'agit d'une page que plusieurs utilisateurs peuvent utiliser pour suivre la progression de leur processus, vous ne le faites pas veulent qu'ils interfèrent avec les alertes des autres utilisateurs.

Cependant, cette nature éphémère de l'intérêt signifie également qu'il y aura des "interruptions" entre les différents appels ajax effectués. Lorsque vous souhaitez écouter plusieurs alertes et que ces alertes peuvent être regroupées très étroitement, il est possible que vous en manquiez une. Supposons que 2 alertes soient espacées de 1 ms :la première sera interceptée, signalée à l'appel ajax, qui devra démarrer un nouvel appel immédiatement afin d'écouter plus d'alertes. Mais comme il n'y avait pas d'auditeur actif pendant ce court laps de temps, cette prochaine alerte a peut-être été manquée. Maintenant, il est probable que ce ne soit un problème que lorsque vous déclenchez plusieurs alertes sous le même gestionnaire. Si vous utilisez plusieurs gestionnaires et lancez des appels ajax pour tous ceux-ci en même temps, ils seront tous traités à temps. Il existe des solutions pour les deux, bien sûr. J'imagine que lorsque vous utilisez un seul gestionnaire, vous pouvez intercepter toutes les alertes d'une collection et vérifier si vous avez déjà envoyé une réponse pour une certaine alerte ou non et si vous souhaitez continuer à vous enregistrer ou non. Avec plusieurs gestionnaires, vous pouvez utiliser un identifiant unique et le suffixer avec différents statuts.

Voici donc un code réel que j'ai utilisé dans mon POC local.

Présentation :J'ai 3 boutons :1 pour générer un identifiant d'alerte, pour lequel j'ai utilisé une séquence. Un autre bouton pour commencer à écouter un événement, et encore un autre bouton pour envoyer une alerte.

Code JS pour le bouton NEW_ALERT_ID :

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

Code JS pour le bouton 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?
    }
});

Code JS pour le bouton SEND_ALERT :

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

Processus de rappel 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;

Donc, je recevais d'abord un identifiant d'alerte, puis je commençais à écouter, puis à un moment donné, j'envoyais une alerte (ou pas). Il s'agit cependant d'un squelette et il faudra encore peaufiner votre configuration actuelle.