sql >> Base de Datos >  >> RDS >> Oracle

¿Cómo usar Oracle DBMS_ALERT dentro de Oracle APEX?

Configuraría una demostración en apex.oracle.com, pero dado que necesita una concesión de ejecución en dbms_alert, tendrá que ser solo textual.

Puede llegar bastante lejos con toda la configuración, por lo que consideraría esto como elementos básicos sobre los que construir. Por ejemplo, solo he trabajado con una alerta. En su muestra, es posible que desee utilizar varios eventos para capturar las diferentes alertas de progreso. Esto es por la simple razón de que para devolver algo al cliente (la respuesta ajax), la devolución de llamada ajax debe 'cerrarse'. Entonces, cuando recibe una alerta y desea devolverla, debe escribir en el búfer y debe devolverse. Esto significa que también dejará de escuchar el evento (léase:¡en ápice, debería hacerlo!).

Considere el flujo de esta manera:realizará una llamada ajax y tendrá un proceso de devolución de llamada ajax que registra el interés en un evento. Luego espera a que se produzca una alerta. Lo captura y lo devuelve escribiéndolo en el búfer http (htp.p ). Ese es el final del código y apex vaciará el búfer, la llamada ajax recogerá la respuesta y podrá administrar ese retorno. Las sesiones no están vinculadas directamente, sino que se reutilizan todo el tiempo. No desea 'dejar' una sesión de base de datos 'sucia'. También tendrá que cancelar el registro de su alerta de interés. Esto también justifica el uso de ID únicos para alertas:las alertas se pueden registrar en diferentes sesiones (de base de datos), por lo que si esta fuera una página que varios usuarios pueden usar para seguir el progreso de su proceso, no quieren que interfieran con las alertas de otros usuarios.

Sin embargo, esta naturaleza fugaz de interés también significa que habrá "interrupciones" entre las diferentes llamadas ajax realizadas. Cuando desea escuchar múltiples alertas, y estas alertas pueden estar empaquetadas muy juntas, existe la posibilidad de que se pierda una. Digamos que 2 alertas están separadas por 1 ms:la primera será capturada, informada a la llamada ajax, que tendría que iniciar una nueva llamada de inmediato para escuchar más alertas. Pero dado que no hubo un oyente activo durante ese corto tiempo, es posible que se haya perdido la siguiente alerta. Ahora, es probable que esto solo sea un problema en el que activa múltiples alertas bajo el mismo controlador. Si usa varios controladores e inicia llamadas ajax para todos al mismo tiempo, todos se manejarán a tiempo. Hay soluciones para ambos, por supuesto. Me imagino que al usar un solo controlador, podría capturar todas las alertas en una colección y verificar si ya envió una respuesta para una alerta determinada o no, y si debe continuar con el registro o no. Con múltiples controladores, puede usar una identificación única y sufijarla con diferentes estados.

Así que aquí hay un código real que he usado en mi POC local.

Descripción general:tengo 3 botones:1 para generar una identificación de alerta, para lo cual usé una secuencia. Otro botón para comenzar a escuchar un evento y otro botón para enviar una alerta.

Código JS para el botón NEW_ALERT_ID:

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

Código JS para el botón 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?
    }
});

Código JS para el botón SEND_ALERT:

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

Procesos de devolución de llamada AJAX:

NUEVA_ALERTA:

htp.p('{"alertId":'||alert_seq.nextval()||'}');

ESCUCHAR_ALERTA:

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;

ENVIAR_ALERTA:

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;

Entonces, primero obtendría una ID de alerta, luego comenzaría a escuchar y luego, en algún momento, enviaría una alerta (o no). Sin embargo, es un esqueleto y necesitará más refinamiento en su configuración real.