Terzo di tre articoli su JasperReports, una libreria Java Open Source che consente la generazione dinamica di report a partire da una fonte dati e la successiva renderizzazione in svariati formati, tra i quali PDF ed HTML. In questa parte conclusiva, vediamo un esempio di creazione di un JRDataSource custom e di utilizzo di iReport per la creazione visuale dei layout JRML.
Introduzione
In questo ultima parte vedremo come è possibile creare una classe JRDataSource  ad hoc per le nostre 
applicazioni e una breve descrizione di come utilizzare iReport, un‘applicazione Open Source simile a 
Crystal Report, per la creazione visuale dei layout JRML.
Creazione di un JRDataSource custom
Nel precedente articolo abbiamo visto un esempio pratico di utilizzo delle API JasperReports 
all‘interno di un‘applicazione Java. Nel layout JRML dell‘esempio era presente il TAG 
nel quale avevamo definito una query SQL con cui recuperare i dati da visualizzare nel report. Al 
momento della creazione del report quindi bisognava aprire una connessione verso il database 
applicativo e passarla come parametro al metodo JasperFillManager.fillReportToFile(). Tale metodo 
esegue la query sul database al momento della creazione del report e tiene occupata la connessione 
finchà© il report non è stato creato. Questa modo di procedere potrebbe però causare perdite di 
performance nei casi in cui i dati siano tanti e il layout parecchio complesso. Ma abbiamo 
un‘alternativa: JasperReports prevede delle classi che fungono da contenitori dei dati e che evitano 
di dovere eseguire la query esclusivamente al momento della creazione e di tenere occupata una 
connessione verso il database per l‘intero processo di creazione. Le classi in questione, appartenenti 
tutte al package net.sf.jasperreports.engine.data, sono le seguenti:
- JRBeanArrayDataSource
 
- JRBeanCollectionDataSource
 
- JREmptyDataSource
 
- JRMapArrayDataSource
 
- JRMapCollectionDataSource
 
- JRResultSetDataSource
 
- JRTabelModelDataSource
 
- JRXMLDataSource
 
Tutte implementano l‘interfaccia net.sf.jasperreports.engine.JRDataSource. Sono dei contenitori di 
dati specializzati, come si evince dai nomi. Ad un report, al momento della creazione, si passa come 
parametro la classe JRDataSource utilizzata e già  popolata con i dati necessari, recuperati dalla 
fonte già  in precedenza.
Le applicazioni in cui verrà  integrato JasperReports sicuramente prevedono delle loro classi 
contenitori dei dati. Piuttosto che implementare una logica che travasi i dati, prima della 
generazione di ogni report, dalla classe contenitore dell‘applicazione ad uno dei JRDataSource già  
previsti in JasperReports, è più agevole implementare un JRDataSource che fa uso direttamente della 
nostra classe contenitore. Nell‘interfaccia JRDataSource sono dichiarati solo due metodi:
public Object getFieldValue( JRField jrField ) throws JRException;
il quale restituisce il valore del campo del report passato in ingresso al metodo e
public boolean next() throws JRException;
il quale non fa altro che incrementare il puntatore all‘interno della struttura dati.
La nostra classe di esempio, MyDataSource, dovrà  quindi implementare l‘interfaccia JRDataSource:
public class MyDataSource implements JRDataSource
e dovremo implementare i due metodi in essa dichiarati. Il JRDataSource che andremo a realizzare 
ingloberà  anche il nostro contenitore dei dati (nell‘esempio, per semplicità , sarà  un array 
bidimensionale, ma è possibile utilizzare qualsiasi tipo di classe):
private Object[][] data ={{ "Bologna", new Integer(22), "Luca Rizzoli", "Via Ugo Bassi, 2" },{ "Bologna", new Integer(9), "Gabriele Marconi", "Via Rizzoli, 87" },{ "Roma", new Integer(32), "Mario Rossi", "Via Flaminia, 6"},{ "Roma", new Integer(23), "Cesare Ottaviano", "Via Ostiense, 12" }};
L‘implementazione del metodo next dovrà  prevedere l‘incremento del puntatore all‘interno della 
struttura dati:
private int lIntIndex = -1;public boolean next() throws JRException {lIntIndex++;return ( lIntIndex < data.length );}
Nel metodo getFieldValue dovremo stabilire l‘implementazione delle regole di recupero dei valori dei 
campi del record. Il corpo di questo metodo varia a seconda della struttura dati utilizzata. Nel 
nostro esempio:
public Object getFieldValue( JRField field ) throws JRException {Object lValue = null;String lStrFieldName = field.getName();if ( "city".equals( lStrFieldName ) ){lValue = data[lIntIndex][0];}else if ( "id".equals( lStrFieldName ) ){lValue = data[lIntIndex][1];}else if ( "name".equals( lStrFieldName ) ){lValue = data[lIntIndex][2];}else if ( "street".equals(lStrFieldName) ){lValue = data[lIntIndex][3];}return lValue;}
city, id, name, e street sono i nomi indicati per i campi nel layout JRML.
Vediamo come sarà  il codice della classe che, a partire dal layout JRML, compilerà  e genererà  un 
report a partire da un‘istanza di MyDataSource. Nel metodo createReport si avrà  una sequenza di 
istruzioni simile a quella dell‘omonimo metodo della classe di esempio dell‘articolo precedente. In 
questo caso però passeremo, fra i parametri del report, quello (DataFile) con l‘indicazione della 
classe che abbiamo intenzione di usare come JRDatasource:
Map lParameters = new HashMap();lParameters.put( "ReportTitle", "My Report" );lParameters.put( "DataFile", "MyDataSource.java" );
Quindi bisogna richiamare, per la generazione del report, il metodo JasperFillManager.fillReportToFile 
che prevede come terzo parametro, rispetto alla firma utilizzata nell‘esempio del mese scorso, 
un‘istanza di JRDataSource invece che una java.sql.Connection:
// creazione del report (file .jrprint)JasperFillManager.fillReportToFile( pStrArgs[1], lParameters, new MyDataSource() );
Il metodo createReport completo è quindi il seguente:
private int createReport( String[] pStrArgs ) {int lIntRet = 0;long lLngStart = System.currentTimeMillis();// compilazione del report (creazione del .jasper a partire dal .jrxml)try{JasperCompileManager.compileReportToFile( pStrArgs[0] );}catch( Exception jre ){System.out.println( "createReport(): eccezione: " + jre.getMessage() );return -1;}System.out.println( "createReport(): Compilazione (ms): " +( System.currentTimeMillis() - lLngStart ) );Map lParameters = new HashMap();lParameters.put( "ReportTitle", "My Report" );lParameters.put( "DataFile", "MyDataSource.java" );try{// creazione del report (file .jrprint)JasperFillManager.fillReportToFile( pStrArgs[1], lParameters, new MyDataSource() );// export del report in PDFJasperExportManager.exportReportToPdfFile( pStrArgs[2],      pStrArgs[3] );System.out.println( "createReport(): Creazione del PDF (ms): " +( System.currentTimeMillis() - lLngStart ) );}catch( Exception ex ){System.out.println( "createReport(): eccezione: " +ex.getMessage() );return -2;}System.out.println( "createReport(): Durata totale (ms): " + ( System.currentTimeMillis() - lLngStart ) );return lIntRet;}
Il report generato è mostrato in figura 1.
L‘esempio completo è allegato all‘articolo.
iReport per la creazione dei layout
La parte che può risultare più "noiosa" in fase di design è la definizione dei layout per i report 
generati dalle applicazioni da noi realizzate. Piuttosto che scrivere a mano i file .jrml è 
sicuramente più comodo utilizzare iReport, un software Open Source di reportistica (il cui project 
leader è un italiano, Giulio Toffoli [2]) realizzato interamente in Java e basato su JasperReports. 
iReport non necessita di installazione: basta scompattare in una directory qualsiasi il contenuto 
dell‘archivio ZIP scaricabile dal sito ufficiale (http://ireport.sourceforge.net). Per avviare iReport 
basta eseguire iReport.bat (su sistemi operativi Microsoft) o iReport.sh (su distribuzioni Linux). Una 
volta avviata l‘applicazione, dal menu Datasource --> Connections/DataSources, bisogna creare una 
connessione al database applicativo (figura 2). 
Quindi va creato un nuovo documento. Gli elementi del report vanno inseriti in maniera visuale. E‘ 
possibile in ogni momento creare il report prima compilando, tramite la voce di menu Build -> Compile, 
e quindi eseguirlo per avere un‘anteprima dell‘aspetto finale con i dati, tramite la voce di menu 
Build -> Execute report (using active conn.). L‘anteprima può essere visualizzata in iReport stesso 
tramite il suo visualizzatore interno (figura 3), oppure salvata su file in uno dei formati previsti 
da JasperReports. Quando si è soddisfatti del layout, lo si salva con estensione .jrml. Questo file 
potrà  poi essere utilizzato dalla nostra applicazione Java come spiegato nell‘articolo precedente.
Conclusioni
Abbiamo così terminato la panoramica su JasperReports. La potenzialità  di queste API è enorme. 
Accoppiando all‘adozione di JasperReports anche l‘utilizzo di iReport per la definizione dei layout, i 
risultati che si ottengono sono molto pregevoli. JasperReports e iReport sono l‘ennesima risposta a 
tutti coloro che purtroppo pensano ancora che tutto ciò che è Open Source non possa dare risultati 
validi in ambito professionale.
Bibliografia
[1] Teodor Danciu - "Documentazione ufficiale di JasperReports"
http://jasperreports.sourceforge.net/
[2] Giulio Toffoli - "Documentazione ufficiale di iReports", JasperSoft, 
http://ireport.sourceforge.net/
Guglielmo Iozzia si è Laureato nel 1999 in Ingegneria Elettronica (indirizzo Biomedico) presso l‘Università di Bologna. Ha progettato e realizzato un software diagnostico per la predizione dell‘andamento della pressione intracranica in pazienti in terapia intensiva neurochirurgica. Frequenta il mondo Java dall‘inizio del 2000. Dopo numerose esperienze presso un‘azienda di Bologna del settore IT (fino all‘aprile del 2006), e per qualche mese in una analoga società di Roma, ha scelto la libera professione, lavorando per RAI Net fino ai primi mesi del 2008. In seguito è diventato Senior IT Consultant per la FAO (Food and Agriculture Organization of the United Nations). In ambito FAO ha dedicato quasi tutto il 2012 al progetto GRMS (Global Resources Management System) in qualità di "Web Services and cross-environmental integration specialist". Da luglio 2013 si è trasferito a Dublino, dove ricopre il ruolo di SVT Automation Engineer per IBM Ireland.