Production massive de rapports

Introduction

BIRT nous a permit d'élaborer nos modèles de rapports avec la mise en page qui nous convient. Nous sommes à même de produire un rapport sur une commune donnée. Néanmoins, il reste à générer des rapports pour toutes les communes du département. Il est clair qu'on ne va pas le faire à la main car ça signifie faire au moins 360 clics pour le département de Maine-et-Loire.

Il faut donc trouver un moyen d'automatiser le processus.

Une solution consiste à utiliser le viewer BIRT qui est disponible dans le fichier de runtime de BIRT. Il s'agit d'une application J2EE qui génère un rapport en fonction des paramètres d'entrées et du fichier de modèle de rapport. L'intérêt de ce viewer est qu'on peut l'interroger par un script.

Une autre solution consiste à développer un script qui utilise le moteur de rapport de BIRT. En effet, ce dernier permet à toute application Java de produire des rapports. C'est une solution tout à fait envisageable et qui serait d'ailleurs préférrable: les personnes qui utilisent BIRT utilisent également Eclipse qui est prévu pour faire du développement JAVA. De plus, avec le script sous la main, les personnes peuvent gérer elles-même leurs productions...

Méthode avec développement

La démarche

BIRT dispose d'un moteur de rapport (BIRT Report Engine). il est possible d'utiliser ce moteur de rapport directement dans une application JAVA. Nos besoins sont ultrasimples:

  • Disposer d'un outil permettant de paramétrer (dans le code): le rapport et les valeurs des paramètres.
  • Faire varier la valeur de ces paramètres suivant une plage donnée.
  • Pour chaque occurence du paramètre variable, produire un rapport dans un format donné !

D'un point de vue algorithme, on a vu plus complexe. Nous allons donc développer un script en Java qui utilise le moteur de rapport de BIRT. Ca tombe finalement assez bien car BIRT est basé sur Eclipse qui est une plateforme de développement initialement créée pour coder en Java !!!

Pré-requis: installation du runtime

Vous devez d'abord télécharger le paquet du runtime BIRT sur la page de téléchargement de BIRT. Ce fichier est une archive compressée de près de 100Mo. Ca peut paraître surprenant car nous disposons déjà de ce moteur dans Eclipse lors de l'installation de BIRT. Néanmoins, notre script va s'appuyer sur ce moteur et vous devez, non seulement, disposer des bonnes bibliothèques (JAR) mais aussi d'indiquer, dans la configuration du moteur de rapport, où sont situées ces bibliothèques.

Vous devez donc télécharger et décompresser cette archive dans un répertoire donné. Pour la suite, ce répertoire sera /opt/BIRT_RE/. La démarche est la même sous MS-Windows mais le nom du répertoire sera différent.

La décompression extrait l'arborescence suivante:

birt-runtime-3_7_2
|
\-------------------ReportEngine
|           |
|           \-----lib
|           \-----samples
|
|
\-------------------WebViewerExample

Globalement, ce qui nous intéresse est dans le répertoire ReportEngine. Nous verrons qu'il sera utilisé dans le source du script de génération massive.

Source du script

Voici le code source d'un script qui est à minima paramétrable. Attention, ça juste marche dans Eclipse et je ne suis pas un développeur Java très expérimenté. En plus, j'avais moins d'une demi-journée à consacrer à ce travail donc c'est du code pas très propre (pas très bien organisé) mais ça correspond à mes besoins simples et c'est facilement récupérable, même pour un non développeur Java (typiquement un simple utilisateur de BIRT).

L'idée principale est de disposer d'une fonction paramétrable qui permet de produire des rapports:

  • dans un format donné
  • dans un répertoire ciblé
  • avec des noms pré-formatés
  • en utilisant toutes les valeurs d'un paramètre (il y aura donc autant de rapports que de valeurs de paramètre).
  // Script pour générer autant de documents que de valeurs de paramètre différent
  import java.util.Collection;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.logging.Level;
  import org.eclipse.birt.core.framework.Platform;
  import org.eclipse.birt.report.engine.api.EngineConfig;
  import org.eclipse.birt.report.engine.api.EngineException;
  import org.eclipse.birt.report.engine.api.IGetParameterDefinitionTask;
  import org.eclipse.birt.report.engine.api.IParameterDefnBase;
  import org.eclipse.birt.report.engine.api.IParameterGroupDefn;
  import org.eclipse.birt.report.engine.api.IParameterSelectionChoice;
  import org.eclipse.birt.report.engine.api.IRenderOption;
  import org.eclipse.birt.report.engine.api.IReportEngine;
  import org.eclipse.birt.report.engine.api.IReportEngineFactory;
  import org.eclipse.birt.report.engine.api.IReportRunnable;
  import org.eclipse.birt.report.engine.api.IRunAndRenderTask;
  import org.eclipse.birt.report.engine.api.RenderOption;
  import org.eclipse.birt.report.engine.api.IScalarParameterDefn;
  import org.eclipse.birt.report.model.api.CascadingParameterGroupHandle;
  import org.eclipse.birt.report.model.api.ScalarParameterHandle;

  public class MassiveBirt {

    @SuppressWarnings("unchecked")
    public void runReport(String path, String filename, String format, String parameter) throws EngineException {
      IReportEngine engine=null;
      EngineConfig config = null;
      IRunAndRenderTask task=null;
      HashMap parmValues = new HashMap();

     try {
       config = new EngineConfig( );
       config.setBIRTHome("/opt/BIRT_RE/birt-runtime-3_7_2/");
       config.setLogConfig(null, Level.OFF);
       Platform.startup( config );
       IReportEngineFactory factory = (IReportEngineFactory) Platform.createFactoryObject( IReportEngineFactory.EXTENSION_REPORT_ENGINE_FACTORY );
       engine = factory.createReportEngine( config );
     } catch( Exception ex){
       ex.printStackTrace();
     }

     IReportRunnable design = null;

     // Ouverture du rapport
     design = engine.openReportDesign("/opt/BIRT_RE/birt-runtime-3_7_2/ReportEngine/rapports/test_RE.rptdesign"); 

     // Récupération de la liste des paramètres
     // Attention, si vos paramètres sont sous forme de groupes, ça ne fonctionnera pas forcément
     IGetParameterDefinitionTask param_task = engine.createGetParameterDefinitionTask( design );
     Collection params = param_task.getParameterDefns( true );
     Iterator iter = params.iterator( );

     while ( iter.hasNext( ) )
     {
       IParameterDefnBase param = (IParameterDefnBase) iter.next( );
       IScalarParameterDefn scalar = (IScalarParameterDefn) param;

       //Si c'est le paramètre qu'on cherche
       if(param.getName().equals(parameter)) {
         // Si le paramètre est une liste déroulante
         if(scalar.getControlType() ==  IScalarParameterDefn.LIST_BOX) {
           Collection selectionList = param_task.getSelectionList( param.getName() );
           //On remplit un HashMap avec les valeurs et les identifiants
           if ( selectionList != null ) {
             for ( Iterator sliter = selectionList.iterator( ); sliter.hasNext( ); ) {
               IParameterSelectionChoice selectionItem = (IParameterSelectionChoice) sliter.next( );
               String value = (String)selectionItem.getValue( );
               String label = selectionItem.getLabel( );
               parmValues.put(value, label);
             } 
           }        
         }   
       }
     }

     param_task.close();

     // Préparation de la génération du rapport
     IRenderOption options = new RenderOption();
     options.setOutputFormat(format);

     // Boucle de génération
     for (Iterator it = parmValues.keySet().iterator(); it.hasNext();) {
       task = engine.createRunAndRenderTask(design);
       Object key = it.next();
       Object value = parmValues.get(key);
       String fullname=path+filename+key+"-"+value+"."+format;
       System.out.println("Generating "+fullname+" ...");
       options.setOutputFileName(fullname);
       task.setParameterValue(parameter, key);
       task.validateParameters();

       // Lancement de la génération
       task.setRenderOption(options);
       task.run();
       task.close();
    }


    // On ferme tout proprement
    engine.destroy();
    Platform.shutdown();
    System.out.println("Finished in "+path);
    System.exit(0);
  }

  public static void main(String[] args) {
    try {
      MassiveBirt ex = new MassiveBirt( );
      ex.runReport("/opt/BIRT_RE/birt-runtime-3_7_2/ReportEngine/", "Fiche_Agricole_",
                   "odt","Commune");
    }
    catch ( Exception e ) {
      e.printStackTrace();
    }
  }

}

Installer le script

Nous avons donc un fichier source Java (le script ci-dessus) ainsi que le runtime BIRT déployé dans un répertoire (/opt/BIRT_RE/...). Nous devons créer un projet Java dans Eclipse et le compiler.

D'abord, assurez-vous de disposer d'une perspective Java:

perspective Java

Ensuite, créer un nouveau projet:

Création d'un projet JAVA dans Eclipse

Ensuite, il faut ajouter les bibliothèques du ReportEngine:

Ajout bibliothèque ReportEngine

Puis, vous devez créer une nouvelle classe nommée MassiveBirt (vous pouvez la laisser dans le package par défaut) et y copier le code ci-dessus:

Nouvelle classe

Enfin, vous devrez adapter la fin du script avec les paramètres indiqués pour que ça fonctionne chez vous !

Génération des rapports

Attention, la génération de rapports est un processus gourmand en mémoire (en fait c'est le cas avec JAVA en général). Avec 2Go de RAM sur une machine et un rapport de seulement une page, la génération de mes 300 rapports déclenche l'utilisation du swap.

Au delà de cet aspect, si vous avez intégré les bons arguments d'appel du script, vous devriez retrouver tous vos rapports dans le répertoire indiqué.

Références

Voici les références en ligne que j'ai utilisé pour découvrir l'API de BIRT:


Méthode sans développement

La démarche

  • Installer Tomcat
  • Installer le viewer BIRT sous Tomcat
  • Elaborer le script de production des rapports
  • Récupérer les fichiers produits

Installer Tomcat

Il existe un tas d'articles sur Internet qui expliquent comment installer un server Tomcat.

TODO