Java et Business Intelligence avec JasperSofts

Pour faire suite à mon article sur la manipulation de documents Office avec Java et rester dans le domaine de l'informatique décisionnelle, voici une présentation de l'outil JasperReports et de son éditeur graphique iReport de la société JasperSofts. Cet exemple se base sur la dernière version (3.7.6 à l'heure de l'écriture) qui offre de nombreuses facilités quant à la mise en place de rapports.

Je vais fournir dans cette article tout le code et les fichiers nécessaires à reproduire l'exemple. En pièce jointe se trouve un lien vers le workspace que j'ai utilisé (diminué des librairies nécessaires à JasperReports pour des raisons de volume d'upload). Je vais donc décrire ici les quelques subtilités que j'ai rencontrées mais le rapport mis en place se compose de la majorité des éléments couramment recherchés (sous-rapport, diagrammes, listes, tableaux croisés, styles, temps d'évaluation...), donc il vous suffira de l'explorer de fond en comble pour trouver votre bonheur! :)

J'ai choisi de créer un rapport au format PDF car à mon sens il est celui qui propose le meilleur rendu pour les différents composants disponibles. Les objets utilisés dans le code sont volontairement simplistes pour ne pas compliquer la compréhension de leur organisation et de leur utilisation dans le modèle.

Voici un aperçu du rapport final créé pour cet article :

Regardons le code de la classe principale qui permet de le générer (Reporter.java) :

package com.developpef;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import net.sf.jasperreports.engine.JasperExportManager;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
import net.sf.jasperreports.engine.util.FileResolver;

public class Reporter {

private static final String ENCOURAGEMENT = "Bonne lecture!";

private static final String CLASSIQUE = "Hope this helps...";

public static void main(String[] args) {
 List<TestBean> beans = new ArrayList<TestBean>();
 beans.add(getBean(ENCOURAGEMENT));
 beans.add(getBean(CLASSIQUE));

 try {
  FileResolver fileResolver = new FileResolver() {
   @Override
   public File resolveFile(String fileName) {
    URI uri;
    try {
     uri = new URI(Thread.currentThread()
       .getContextClassLoader().getResource(
          "com/resources/" + fileName).getPath());
     return new File(uri.getPath());
    } catch (URISyntaxException e) {
     e.printStackTrace();
     return null;
    }
   }
  };
  HashMap<Object, Object> parameters = new HashMap<Object, Object>();
  parameters.put("REPORT_FILE_RESOLVER", fileResolver);
  URL jasperModelUrl = Thread.currentThread().getContextClassLoader()
    .getResource("com/resources/jasper/reportTest.jasper");
  URL resourceFolder = Thread.currentThread().getContextClassLoader()
    .getResource("com/resources/jasper/");
  InputStream stream = new FileInputStream(new File(jasperModelUrl
    .toURI()));
  JRBeanCollectionDataSource datasource = new JRBeanCollectionDataSource(beans);
  JasperPrint jasperPrint = JasperFillManager.fillReport(stream,
    parameters, datasource);
  File finalPdfPath = new File(resourceFolder.toURI().getPath()
    + "reportTest.pdf");
  JasperExportManager.exportReportToPdfFile(jasperPrint, finalPdfPath
    .getAbsolutePath());
  Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler "
    + finalPdfPath.getAbsolutePath());
 } catch (Exception e) {
  e.printStackTrace();
 }
}

private static TestBean getBean(String conclusion) {
 TestBean bean = new TestBean();
 bean.setAuthor("Developpef");
 bean.setHomeUrl("http://developpef.blogspot.com/");
 bean.getStatsHolder().getStats().add(new StatBean("Mozilla", 60));
 bean.getStatsHolder().getStats().add(new StatBean("IE", 25));
 bean.getStatsHolder().getStats().add(new StatBean("Others", 15));
 bean.getStatsHolder().getStatsOS().add(new StatBean(502, "Windows"));
 bean.getStatsHolder().getStatsOS().add(new StatBean(158, "Linux"));
 bean.getStatsHolder().getStatsOS().add(new StatBean(12, "Other Unix"));
 bean.getArchives().add(new ArchiveBean("Eclipse", "Septembre", 2));
 bean.getArchives().add(new ArchiveBean("Eclipse", "Octobre", 7));
 bean.getArchives().add(new ArchiveBean("Java", "Septembre", 10));
 bean.getArchives().add(new ArchiveBean("Java", "Octobre", 3));
 bean.getArchives().add(new ArchiveBean("Java", "Novembre", 6));
 bean.getJavaBeans().add(new StatBean(null, 0));
 bean.getJavaBeans().add(new TestBean());
 bean.getJavaBeans().add(new ArchiveBean(null, null, 0));
 bean.getJavaBeans().add(new StatsHolder());
 bean.setJavaTxt1("Ce texte est issu de Java et fait donc partie du contenu "
   + "dynamique du rapport. "
   + "Seuls les quelques POJO listés ci-dessous ont suffit à créer "
   + "tous les graphiques et autres listes présentés ici :");
 bean.setJavaTxt2("Ce rapport assez simple ne se compose que d'une seule page mais est"
   + " édité pour 2 éléments, comme le prouve le code barre basé sur le hashCode"
   + " de ce texte. " + conclusion);
 return bean;
}
}

Tout commence par la création de la liste de beans qui sera passée comme source de données au rapport. Un rapport sera créé pour chaque bean, ce qui dans cet exemple résultera en un PDF de 2 pages. Ces beans doivent être différentiables par le template, c'est la raison pour laquelle il est fortement conseillé de surcharger les méthodes equals() et hashCode() sous peine de voir le rapport répété X fois pour les même données. Ici la distinction se fait grâce à la toute dernière phrase du rapport (variables statiques).

L'en-tête contient une image de fond. Pour permettre au moteur du template de pouvoir la résoudre dans notre classpath et l'insérer dans le PDF, il est indispensable de lui injecter un FileResolver lui permettant d'accéder à tous les fichiers nécessaires (images, sous-rapports...). Pour cela, il faut utiliser le ContextClassLoader afin de parcourir les ressources. Le paramètre fileName correspond au chemin des éléments indiqué dans le template ("images/..." ou "jasper/..").

Il faut également savoir que JasperReports se base sur tout un tas de librairies standards pour créer ses composants. Si vous rencontrez quelques difficultés, il vous suffit de vous rapporter aux documentations respectives :


Fichier(s) joint(s) :

0 commentaires: