Segnalazione BIRT con Spring Boot

1. Introduzione

In questo tutorial, integreremo BIRT (Business Intelligence and Reporting Tools) con Spring Boot MVC, per fornire report statici e dinamici in formato HTML e PDF.

2. Cos'è BIRT ?

BIRT è un motore open source per creare visualizzazioni di dati che possono essere integrate nelle applicazioni web Java.

È un progetto software di alto livello all'interno della Eclipse Foundation e sfrutta i contributi di IBM e Innovent Solutions. È stato avviato e sponsorizzato da Actuate alla fine del 2004.

Il framework consente di creare report integrati con un'ampia gamma di origini dati.

3. Dipendenze di Maven

BIRT ha due componenti principali: un progettista di report visivo per creare file di progettazione di report e un componente runtime per interpretare e rendere tali progetti.

Nella nostra applicazione web di esempio, useremo entrambi sopra Spring Boot.

3.1. Dipendenze del framework BIRT

Poiché siamo abituati a pensare in termini di gestione delle dipendenze, la prima scelta sarebbe cercare BIRT a Maven Central.

Tuttavia, l'ultima versione ufficiale della libreria principale disponibile è la 4.6 del 2016 , mentre nella pagina di download di Eclipse possiamo trovare i collegamenti per almeno due versioni più recenti ( l'attuale è la 4.8 ).

Se scegliamo di utilizzare la build ufficiale, il modo più semplice per avere il codice attivo e funzionante è scaricare il pacchetto BIRT Report Engine, che è un'applicazione web completa utile anche per l'apprendimento. Dobbiamo quindi copiare la sua cartella lib nel nostro progetto (circa 68 MB di dimensione) e dire all'IDE di includere tutti i jar al suo interno.

Inutile dire che, utilizzando questo approccio, saremo in grado di compilare solo tramite l'IDE , poiché Maven non troverà quei jar a meno che non li configuriamo e li installiamo manualmente (più di 100 file!) Nel nostro repository locale.

Fortunatamente, Innovent Solutions ha deciso di prendere in mano la situazione e ha pubblicato su Maven Central le proprie build delle ultime dipendenze BIRT, il che è fantastico, poiché gestisce per noi tutte le dipendenze necessarie.

Leggendo i commenti nei forum online, non è chiaro se questi artefatti siano pronti per la produzione, ma Innovent Solutions ha lavorato al progetto accanto al team di Eclipse sin dall'inizio, quindi il nostro progetto si basa su di loro.

Includere BIRT ora è molto semplice:

 com.innoventsolutions.birt.runtime org.eclipse.birt.runtime_4.8.0-20180626 4.8.0 

3.2. Dipendenze di Spring Boot

Ora che BIRT è importato nel nostro progetto, dobbiamo solo aggiungere le dipendenze standard di Spring Boot nel nostro file pom.

C'è una trappola, tuttavia, perché il jar BIRT contiene la propria implementazione di Slf4J , che non funziona bene con Logback e genera un'eccezione di conflitto durante l'avvio.

Poiché non possiamo rimuoverlo dal jar, per risolvere questo problema, dobbiamo escludere Logback :

 org.springframework.boot spring-boot-starter-logging   ch.qos.logback logback-classic   

Ora siamo finalmente pronti per iniziare!

4. Rapporti BIRT

Nel framework BIRT, un report è un lungo file di configurazione XML , identificato dall'estensione rptdesign .

Indica al motore cosa disegnare e dove , dallo stile di un titolo fino alle proprietà richieste per connettersi a una sorgente dati.

Per un report dinamico di base, dobbiamo configurare tre cose:

  1. la fonte dei dati (nel nostro esempio usiamo un file CSV locale, ma potrebbe facilmente essere una tabella di database)
  2. gli elementi che vogliamo visualizzare (grafici, tabelle, ecc.)
  3. il design della pagina

Il report è strutturato come una pagina HTML, con intestazione, corpo, piè di pagina, script e stili.

Il framework fornisce un ampio set di componenti tra cui scegliere immediatamente , inclusa l'integrazione con le principali origini dati, layout, grafici e tabelle. E possiamo estenderlo per aggiungere il nostro!

Esistono due modi per generare un file di report: visivo o programmatico.

5. Eclipse Report Designer

Per facilitare la creazione di report, il team di Eclipse ha creato un plug-in dello strumento di progettazione di report per il suo popolare IDE.

Questo strumento presenta una semplice interfaccia drag & drop dalla tavolozza a sinistra, che apre automaticamente la finestra di configurazione per il nuovo componente che aggiungiamo alla pagina. Possiamo anche vedere tutte le personalizzazioni disponibili per ogni componente cliccandoci sopra nella pagina e poi sul pulsante Property Editor (evidenziato nell'immagine sotto).

Per visualizzare l'intera struttura della pagina in una visualizzazione ad albero, è sufficiente fare clic sul pulsante Struttura .

La scheda Esplora dati contiene anche le origini dati definite per il nostro rapporto:

Il report di esempio visualizzato nell'immagine è disponibile nel percorso /reports/csv_data_report.rptdesign

Un altro vantaggio di rivolgersi al visual designer è la documentazione in linea, che si concentra maggiormente su questo strumento anziché sull'approccio programmatico.

Se stiamo già utilizzando Eclipse, dobbiamo solo installare il plug-in BIRT Report Design , che include una prospettiva predefinita e l'editor visivo.

Per quegli sviluppatori che attualmente non stanno utilizzando Eclipse e non vogliono cambiare, c'è un pacchetto Eclipse Report Designer , che consiste in un'installazione Eclipse portatile con il plugin BIRT preinstallato.

Una volta che il file di report è stato finalizzato, possiamo salvarlo nel nostro progetto e tornare alla codifica nel nostro ambiente preferito.

6. L'approccio programmatico

Possiamo anche progettare un report utilizzando solo codice , ma questo approccio è molto più difficile a causa della scarsa documentazione disponibile, quindi preparati a scavare nel codice sorgente e nei forum online.

Vale anche la pena considerare che tutti i noiosi dettagli di progettazione come dimensioni, lunghezza e posizione della griglia sono molto più facili da gestire utilizzando il designer .

Per dimostrare questo punto, ecco un esempio di come definire una semplice pagina statica con un'immagine e un testo:

DesignElementHandle element = factory.newSimpleMasterPage("Page Master"); design.getMasterPages().add(element); GridHandle grid = factory.newGridItem(null, 2, 1); design.getBody().add(grid); grid.setWidth("100%"); RowHandle row0 = (RowHandle) grid.getRows().get(0); ImageHandle image = factory.newImage(null); CellHandle cell = (CellHandle) row0.getCells().get(0); cell.getContent().add(image); image.setURL("\"//www.baeldung.com/wp-content/themes/baeldung/favicon/favicon-96x96.png\""); LabelHandle label = factory.newLabel(null); cell = (CellHandle) row0.getCells().get(1); cell.getContent().add(label); label.setText("Hello, Baeldung world!");

Questo codice genererà un rapporto semplice (e brutto):

Il report di esempio visualizzato nell'immagine sopra può essere trovato in questo percorso: /reports/static_report.rptdesign.

Dopo aver codificato come dovrebbe apparire il report e quali dati visualizzare, possiamo generare il file XML eseguendo la nostra classe ReportDesignApplication .

7. Collegamento di un'origine dati

Abbiamo accennato in precedenza che BIRT supporta molte diverse origini dati.

Per il nostro progetto di esempio, abbiamo utilizzato un semplice file CSV con tre voci. Si trova nella cartella dei rapporti ed è composto da tre semplici righe di dati, più intestazioni:

Student, Math, Geography, History Bill, 10,3,8 Tom, 5,6,5 Anne, 7, 4,9

7.1. Configurazione dell'origine dati

Per consentire a BIRT di utilizzare il nostro file (o qualsiasi altro tipo di sorgente), dobbiamo configurare un'origine dati .

Per il nostro file, abbiamo creato un'origine dati file flat con il progettista di report, il tutto in pochi passaggi:

  1. Open the designer perspective and look at the outline on the right.
  2. Right-click on the Data Sources icon.
  3. Select the desired source type (in our case the flat file source).
  4. We can now choose either to load an entire folder or just one file. We used the second option (if our data file is in CSV format, we want to make sure to use the first line as column name indicator).
  5. Test the connection to make sure the path is correct.

We attached some pictures to show each step:

7.2. The Data Set

The data source is ready, but we still need to define our Data Set, which is the actual data shown in our report:

  1. Open the designer perspective and look at the outline on the right.
  2. Right-click on the Data Sets icon.
  3. Select the desired Data Source and the type (in our case there's only one type).
  4. The next screen depends on the type of data source and data set we're selected: in our case, we see a page where we can select the columns to include.
  5. Once the setup is complete, we can open the configuration at any time by double-clicking on our data set.
  6. In Output Columns, we can set the right type of the data displayed.
  7. We can then look at a preview by clicking on Preview Results.

Again, some pictures to clarify these steps:

7.3. Other Data Source Types

As mentioned in step 4 of the Data Set configuration, the options available may change depending on the Data Source referred.

For our CSV file, BIRT gives options related to which columns to show, the data type, and if we want to load the entire file. On the other hand, if we had a JDBC data source, we may have to write an SQL query or a stored procedure.

From the Data Sets menu, we can also join two or more data sets in a new data set.

8. Rendering the Report

Once the report file is ready, we have to pass it to the engine for rendering. To do this, there are a few things to implement.

8.1. Initializing the Engine

The ReportEngine class, which interprets the design files and generates the final result, is part of the BIRT runtime library.

It uses a bunch of helpers and tasks to do the job, making it quite resource-intensive:

Image source: Eclipse BIRT documentation

There is a significant cost associated with creating an engine instance, due primarily to the cost of loading extensions. Therefore, we should create just one ReportEngine instance and use it to run multiple reports.

The report engine is created through a factory supplied by the Platform. Before creating the engine, we have to start the Platform, which will load the appropriate plug-ins:

@PostConstruct protected void initialize() throws BirtException { EngineConfig config = new EngineConfig(); config.getAppContext().put("spring", this.context); Platform.startup(config); IReportEngineFactory factory = (IReportEngineFactory) Platform .createFactoryObject(IReportEngineFactory.EXTENSION_REPORT_ENGINE_FACTORY); birtEngine = factory.createReportEngine(config); imageFolder = System.getProperty("user.dir") + File.separatorChar + reportsPath + imagesPath; loadReports(); }

When we don't need it anymore, we can destroy it:

@Override public void destroy() { birtEngine.destroy(); Platform.shutdown(); }

8.2. Implementing the Output Format

BIRT already supports multiple output formats:HTML, PDF, PPT, and ODT, to name a few.

For the sample project, we implemented two of them with the methods generatePDFReport and generateHTMLReport.

They differ slightly depending on the specific properties needed, such as output format and image handlers.

In fact, PDFs embed images together with text, while HTML reports need to generate them and/or link them.

Thus, the PDF rendering function is quite straightforward:

private void generatePDFReport(IReportRunnable report, HttpServletResponse response, HttpServletRequest request) { IRunAndRenderTask runAndRenderTask = birtEngine.createRunAndRenderTask(report); response.setContentType(birtEngine.getMIMEType("pdf")); IRenderOption options = new RenderOption(); PDFRenderOption pdfRenderOption = new PDFRenderOption(options); pdfRenderOption.setOutputFormat("pdf"); runAndRenderTask.setRenderOption(pdfRenderOption); runAndRenderTask.getAppContext().put(EngineConstants.APPCONTEXT_PDF_RENDER_CONTEXT, request); try { pdfRenderOption.setOutputStream(response.getOutputStream()); runAndRenderTask.run(); } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } finally { runAndRenderTask.close(); } }

While the HTML rendering function needs more settings:

private void generateHTMLReport(IReportRunnable report, HttpServletResponse response, HttpServletRequest request) { IRunAndRenderTask runAndRenderTask = birtEngine.createRunAndRenderTask(report); response.setContentType(birtEngine.getMIMEType("html")); IRenderOption options = new RenderOption(); HTMLRenderOption htmlOptions = new HTMLRenderOption(options); htmlOptions.setOutputFormat("html"); htmlOptions.setBaseImageURL("/" + reportsPath + imagesPath); htmlOptions.setImageDirectory(imageFolder); htmlOptions.setImageHandler(htmlImageHandler); runAndRenderTask.setRenderOption(htmlOptions); runAndRenderTask.getAppContext().put( EngineConstants.APPCONTEXT_BIRT_VIEWER_HTTPSERVET_REQUEST, request); try { htmlOptions.setOutputStream(response.getOutputStream()); runAndRenderTask.run(); } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } finally { runAndRenderTask.close(); } }

Most noteworthy, we set the HTMLServerImageHandler instead of leaving the default handler. This small difference has a big impact on the generated img tag:

  • the default handler links the img tag to the file system path, blocked for security by many browsers
  • the HTMLServerImageHandler links to the server URL

With the setImageDirectory method, we specify where the engine will save the generated image file.

By default, the handler generates a new file at every request, so we could add a caching layer or a deletion policy.

8.3. Publishing the Images

In the HTML report case, image files are external, so they need to be accessible on the server path.

In the code above, with the setBaseImageURL method, we tell the engine what relative path should be used in the img tag link, so we need to make sure that the path is actually accessible!

For this reason, in our ReportEngineApplication, we configured Spring to publish the images folder:

@SpringBootApplication @EnableWebMvc public class ReportEngineApplication implements WebMvcConfigurer { @Value("${reports.relative.path}") private String reportsPath; @Value("${images.relative.path}") private String imagesPath; ... @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry .addResourceHandler(reportsPath + imagesPath + "/**") .addResourceLocations("file:///" + System.getProperty("user.dir") + "/" + reportsPath + imagesPath); } }

Whatever the path we choose, we have to make sure the same path is used here and in the htmlOptions of the previous snippet, or our report won't be able to display images.

9. Displaying the Report

The last component needed to get our application ready is a Controller to return the rendered result:

@RequestMapping(method = RequestMethod.GET, value = "/report/{name}") @ResponseBody public void generateFullReport(HttpServletResponse response, HttpServletRequest request, @PathVariable("name") String name, @RequestParam("output") String output) throws EngineException, IOException { OutputType format = OutputType.from(output); reportService.generateMainReport(name, format, response, request); }

With the output parameter, we can let the user choose the desired format — HTML or PDF.

10. Testing the Report

We can start the application by running the ReportEngineApplication class.

During startup, the BirtReportService class will load all the reports found in the /reports folder.

To see our reports in action, we just need to point our browser to:

  • /report/csv_data_report?output=pdf
  • /report/csv_data_report?output=html
  • /report/static_report?output=pdf
  • /report/static_report?output=html

Here is how the csv_data_report report looks:

To reload a report after changing the design file, we just point our browser to /report/reload.

11. Conclusion

In questo articolo, abbiamo integrato BIRT con Spring Boot, esplorando le insidie ​​e le sfide, ma anche la sua potenza e flessibilità.

Il codice sorgente dell'articolo è disponibile su GitHub.