Expertise Java & J2EE

Astuces et conseils pour le programmeur raffiné

Comment générer des PDFs très longs avec JasperReports [Permalink]

Sat Feb 09 07:37:36 CET 2008

La solution Open Source JasperReports est déjà bien connu et bien respecté dans l'informatique d'entreprise pour générer des reports, sous format .pdf, Excel, html, et autres. Sur un projet récent, on avait cependant un problème: nous nous retrouvions avec un pdf à générer qui faisait plus de 700 pages, et le pauvre serveur chargé de le produire plantait en OutOfMemoryError. Après débogage, on a vu que le coupable était JasperReports lui-même, qui plantait lors de la création du JasperPrint. Ma première réaction était donc d'éviter de créer un JasperPrint, et d'utiliser des méthodes qui permettent de créer un report directement avec des InputStream et OutputStream. Mais l'utilisation d'un PipedInputStream oblige déjà l'utilisation des Threads, ce qui n'est pas strictement permis dans une application J2EE (la gestion des threads étant le domaine du serveur). Mais même avec ces modifications, les problèmes des OutOfMemoryError persistaient. Heureusement la solution s'est ensuite présentée, et donc dans ce petit article, je vais expliquer comment on peut utiliser JasperReports pour créer des documents de n'importe laquelle taille, sans avoir des soucis de mémoire.

La solution qui a finalement marché était moins radicale que cette première idée. En fait on continue à utiliser un JasperPrint, alors les effets sur le reste notre code étaient minimes. Il suffisait en effet d'ajouter les lignes suivantes juste avant sa création:

JRSwapFile swapFile = new JRSwapFile("/tmp/", 0x400, 0x400);
JRAbstractLRUVirtualizer virtualizer = 
  new JRSwapFileVirtualizer(0x40, swapFile, true);
parametres.put(JRParameter.REPORT_VIRTUALIZER, virtualizer);
JasperPrint print = JasperFillManager.fillReport(
  jasper, /* notre fichier .jasper compilé */
  parametres, /* notre Map des paramètres */
  new JRBeanCollectionDataSource(donnees)); /* notre Collection des données */

(La dernière ligne est inchangée de notre classe d'origine, mais je l'inclus pour vous donner le contexte.) Que font ces trois nouvelles lignes? En fait, notre ancienne solution plantait parce que le JasperPrint qu'on gardait en mémoire était trop grand. Les Virtualizer de JasperReports permettent de déclarer des fichiers tampons, qui sont utilisés par Jasper pour écrire une partie de ses données sur disque. En ajoutant un virtualizer dans le Map des paramètres, Jasper va automatiquement se servir de cette fonctionnalité (dans notre cas, en écrivant un fichier tampon dans le dossier temporaire /tmp/; il existe aussi d'autres types de Virtualizer).

Dans notre projet, le simple ajout de ces trois lignes supplémentaires a suffit pour qu'on sorte nos .pdf de plus de 700 pages sans problème. Les vrais experts de JasperReports remarqueront quand même qu'on utilise un JRBeanCollectionDataSource, ce qui implique que nous avons une Collection en mémoire avec nos données. On peut imaginer que, si notre rapport était vraiment énorme (disons 7000 pages), on n'aurait pas assez de mémoire pour stocker toutes ces données en mémoire. En effet, si le volume des données est trop important, il faut implémenter un JRDataSource qui permet de lire les données un à un, au lieu de tout prendre en mémoire à l'avance. (JRResultSetDataSource répond déjà à ce critère.)

Si on fait attention sur ces deux points, d’utiliser un JRDataSource extensible, et d’inclure un REPORT_VIRTUALIZER dans les paramètres fournis au JasperFillManager, on pourra créer des JasperReports extrêmement volumineux sans avoir à se soucier de l'utilisation de mémoire.

Trackback for this entry

http://java.craven.fr/blog/java/Java/?permalink=Comment-generer-des-PDFs-tres-longs-avec-JasperReports.html&tb=y


Powered By blojsom
XML  RSS  RDF