IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Oracle Forms : barre de progression

Date de publication : 30/04/2007

Par Salim Chelabi
 

Comment afficher en temps réel la progression d'une procédure stockée avec Oracle Forms 10g

1. Rappel
2. La procédure de test
3. Le code Forms
4. La forme de test
Idée originale et remerciements

Oracle Forms : Barre de progression
Oracle Forms : Barre de progression

1. Rappel

Lorsque l'on souhaite exécuter une procédure externe (procédure stockée en base, par exemple) depuis un écran Forms, l'application reste en attente de la fin d'exécution de la procédure. En d'autres mots, elle est "gelée" et il n'est pas possible de lui faire exécuter d'autres instructions.
Même les timers ne se sont plus actif durant cette phase.

Les règles de base de l'ergonomie voudraient pourtant que l'utilisateur soit informé de l'avancement de toute tâche nécessitant plus de quelques secondes.

La technique décrite dans cette article permet de suivre et d'afficher l'avancement d'une tâche externe.
Elle utilise à la fois la fonction dbms_application_info.set_session_longops() au sein de la procédure stockée pour mettre à jour en temps réel son compteur d'avancement, et également la fonction dbms_scheduler.create_job() pour lancer la procédure en background et de manière asynchrone.


2. La procédure de test

Afin de tester la solution, nous allons créer une procédure en base qui ne fait rien d'autre que passer le temps...
create or replace procedure Progress_Bar 
As
   rindex pls_integer := -1;
   slno   pls_integer;
Begin
  -----------------------------------------------------
  --  procedure that does nothing else than waiting  --
  --  to illustrate the ProgressBar Forms sample     --
  -----------------------------------------------------
   dbms_application_info.set_session_longops(
     RINDEX      => rindex
    ,SLNO        => slno
    ,OP_NAME     => 'PROGRESS_BAR'
    ,SOFAR       => 0
    ,TOTALWORK   => 100
   );
   --  simulating the task progress  --
   For i IN 1..100 loop
     DBMS_LOCK.SLEEP(.3);
     dbms_application_info.set_session_longops(
       RINDEX      => rindex
      ,SLNO        => slno
      ,OP_NAME     => 'PROGRESS_BAR'
      ,SOFAR       => i
      ,TOTALWORK   => 100
     );
   End loop;
End;
La boucle permet de simuler une procédure d' une minute.
L'index (SOFAR) est mis à jour environ chaque seconde.


3. Le code Forms

Declare
  v_jobname  Varchar2(30) := 'PROGRESS_BAR_JOB' ;
  v_jobid    Number := 12345 ;
  v_percent  Number :=  0 ;
  v_end      Exception ;
  v_version  Number := 9 ;
  v_nb       Pls_integer ;
  v_pass     Pls_integer := 0 ;	
begin 
  -- Lancement de la procédure par le package dbms_job pour la version 9i --
  If v_version = 9 Then
    dbms_job.isubmit(v_jobid,'Progress_Bar;',sysdate,null);
    forms_ddl('commit') ;
  Else
  -- Lancement de la procédure par le package dbms_scheduler pour la version 10g --
  dbms_scheduler.create_job(
    job_name   => v_jobname
   ,job_type   => 'stored_procedure'
   ,job_action => 'Progress_Bar'
   ,start_date => SYSDATE
   ,enabled    => TRUE
  );
  End if ;
  
  -- le job tourne ? --
  Loop 
     If v_version = 9 Then
       Select count(job)
       Into   v_nb 
       From   user_jobs
       Where  job= v_jobid 
       And    total_time!=0;
     Else
       Select count(*) 
       Into   v_nb 
       From   USER_SCHEDULER_JOBS 
       Where  JOB_NAME = v_jobname;
     End if ;
     
     v_pass := v_pass + 1 ;
     If v_pass > 100 Then
     	  -- le job ne se lance pas --
     	  message('Problème de lancement du job',acknowledge);
     	  Raise Form_Trigger_Failure ;
     End if ;
     exit when v_nb > 0  ;
     dbms_lock.sleep(.2);
  End loop ;
  
  set_item_property('blo_progress.progressbar', width, 0);
  v_percent :=0;
  
  -- pour suivre la progression de l'éxecution de procedure --
  Loop    
      
      Exit when v_percent >= 100;
      Select  (sofar / totalwork) * 100
      Into    v_percent
      From    v$session_longops
      Where   opname = 'PROGRESS_BAR' and sofar < totalwork;
		   
      :blo_progress.progressbar:= v_percent||'%';
      set_item_property('blo_progress.progressbar',width, round( v_percent*2,2));
      synchronize;
     
  End loop;

  raise v_end ;
  
Exception
   When NO_DATA_FOUND then
      set_item_property('blo_progress.progressbar', width, 200);
      :blo_progress.progressbar:= '100%';
      raise v_end ;
   When TOO_MANY_ROWS then
      raise v_end ;
   When v_end then
      If v_version = 10 Then
        DBMS_SCHEDULER.drop_job (job_name => v_jobname,FORCE=> true) ;   
      End if ;
   When Others then
      If v_version = 10 Then
        DBMS_SCHEDULER.drop_job (job_name => v_jobname,FORCE=> true) ;   
      End if ;
End;

Selon la version de la base de données l'une des 2 procédures suivantes est utilisée:

  • Oracle 9i : Dbms_Job
  • Oracle 10g : Dbms_Scheduler
La barre de progression est simulée avec un simple Text Item dont la longeur de zéro au départ est augmentée pendant l'exécution.
Le pourcentage d'avancement de la procédure est lu depuis la vue v$session_longops, puis la longueur de la barre de progression est augmentée de la valeur * 2.
A la fin de la procédure, qui met à jour 100 fois son état d'avancement, la barre de progression atteint donc une largeur de 200 pixels.


4. La forme de test

Un fichier zip contient le code de la procédure stockée ainsi que la forme de test 10gR2 (10.1.2) : progressbar.fmb
progressbar.zip


Idée originale et remerciements

L'idée originale de l'utilisation de la fonction dbms_application_info.set_session_longops() pour résoudre ce problème provient de l'article original allemand suivant :
Prozesslogik "im Hintergrund" mit Fortschrittsbalken


Chaleureux remerciements à Developpez.com et l'équipe SGBD



Valid XHTML 1.1!Valid CSS!

Copyright © 2007 . Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.