BIRT Reporting mit Spring Boot

1. Einleitung

In diesem Tutorial werden wir BIRT (Business Intelligence- und Berichterstellungstools) in Spring Boot MVC integrieren, um statische und dynamische Berichte im HTML- und PDF-Format bereitzustellen.

2. Was ist GEBURT ?

BIRT ist eine Open Source Engine zum Erstellen von Datenvisualisierungen , die in Java-Webanwendungen integriert werden können.

Es handelt sich um ein Top-Level-Softwareprojekt innerhalb der Eclipse Foundation, das Beiträge von IBM und Innovent Solutions nutzt. Es wurde Ende 2004 von Actuate gestartet und gesponsert.

Das Framework ermöglicht die Erstellung von Berichten, die in eine Vielzahl von Datenquellen integriert sind.

3. Maven-Abhängigkeiten

BIRT besteht aus zwei Hauptkomponenten: einem visuellen Berichtsdesigner zum Erstellen von Berichtsentwurfsdateien und einer Laufzeitkomponente zum Interpretieren und Rendern dieser Entwürfe.

In unserer Beispiel-Webanwendung verwenden wir beide zusätzlich zu Spring Boot.

3.1. BIRT Framework-Abhängigkeiten

Da wir es gewohnt sind, in Bezug auf das Abhängigkeitsmanagement zu denken, besteht die erste Wahl darin, in Maven Central nach BIRT zu suchen.

Die neueste offizielle Version der verfügbaren Kernbibliothek ist jedoch 4.6 aus dem Jahr 2016 , während auf der Eclipse-Download-Seite Links für mindestens zwei neuere Versionen zu finden sind ( die aktuelle Version ist 4.8 ).

Wenn Sie sich für den offiziellen Build entscheiden, können Sie den Code am einfachsten zum Laufen bringen, indem Sie das BIRT Report Engine-Paket herunterladen, eine vollständige Webanwendung, die auch zum Lernen nützlich ist. Wir müssen dann den lib- Ordner in unser Projekt kopieren (ungefähr 68 MB groß) und die IDE anweisen, alle Jars darin aufzunehmen.

Es versteht sich von selbst, dass wir mit diesem Ansatz nur über die IDE kompilieren können , da Maven diese Jars nur findet, wenn wir sie manuell (mehr als 100 Dateien!) In unserem lokalen Repo konfigurieren und installieren.

Glücklicherweise hat Innovent Solutions beschlossen, die Angelegenheit in die Hand zu nehmen, und auf Maven Central seine eigenen Builds der neuesten BIRT-Abhängigkeiten veröffentlicht. Dies ist großartig, da es für uns alle erforderlichen Abhängigkeiten verwaltet.

Beim Lesen von Kommentaren in Online-Foren ist unklar, ob diese Artefakte produktionsbereit sind, aber Innovent Solutions hat von Anfang an neben dem Eclipse-Team an dem Projekt gearbeitet, sodass sich unser Projekt auf sie stützt.

Das Einschließen von BIRT ist jetzt sehr einfach:

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

3.2. Spring Boot-Abhängigkeiten

Nachdem BIRT in unser Projekt importiert wurde, müssen wir nur noch die Standard-Spring Boot-Abhängigkeiten in unsere POM-Datei einfügen.

Es gibt jedoch eine Gefahr, da das BIRT-JAR eine eigene Implementierung von Slf4J enthält , die mit Logback nicht gut funktioniert und beim Start eine Konfliktausnahme auslöst .

Da wir es nicht aus dem Glas entfernen können, müssen wir Logback ausschließen , um dieses Problem zu beheben :

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

Jetzt können wir endlich loslegen!

4. BIRT-Berichte

Im BIRT-Framework ist ein Bericht eine lange XML-Konfigurationsdatei , die durch die Erweiterung rptdesign gekennzeichnet ist .

Es teilt der Engine mit, was und wo gezeichnet werden soll , vom Stil eines Titels bis zu den erforderlichen Eigenschaften für die Verbindung mit einer Datenquelle.

Für einen grundlegenden dynamischen Bericht müssen drei Dinge konfiguriert werden:

  1. die Datenquelle (in unserem Beispiel verwenden wir eine lokale CSV-Datei, aber es könnte leicht eine Datenbanktabelle sein)
  2. die Elemente, die wir anzeigen möchten (Diagramme, Tabellen usw.)
  3. das Seitendesign

Der Bericht ist wie eine HTML-Seite mit Kopfzeile, Text, Fußzeile, Skripten und Stilen aufgebaut.

Das Framework bietet eine umfangreiche Auswahl an sofort einsatzbereiten Komponenten , einschließlich der Integration in gängige Datenquellen, Layouts, Diagramme und Tabellen. Und wir können es erweitern, um unser eigenes hinzuzufügen!

Es gibt zwei Möglichkeiten, eine Berichtsdatei zu erstellen: visuell oder programmatisch.

5. Der Eclipse Report Designer

Um die Erstellung von Berichten zu vereinfachen, hat das Eclipse-Team ein Plugin für das Berichtsdesign-Tool für seine beliebte IDE erstellt.

Dieses Tool verfügt über eine einfache Drag & Drop-Oberfläche aus der Palette auf der linken Seite, die automatisch das Setup-Fenster für die neue Komponente öffnet, die wir der Seite hinzufügen. Sie können auch alle für jede Komponente verfügbaren Anpassungen anzeigen, indem Sie auf der Seite darauf und dann auf die Schaltfläche Eigenschafteneditor klicken (im Bild unten hervorgehoben).

Um die gesamte Seitenstruktur in einer Baumansicht zu visualisieren, müssen Sie nur auf die Schaltfläche Gliederung klicken .

Die Registerkarte Daten-Explorer enthält auch die für unseren Bericht definierten Datenquellen:

Der im Bild angezeigte Beispielbericht befindet sich unter dem Pfad /reports/csv_data_report.rptdesign

Ein weiterer Vorteil des visuellen Designers ist die Online-Dokumentation, die sich mehr auf dieses Tool als auf den programmatischen Ansatz konzentriert.

Wenn wir Eclipse bereits verwenden, müssen wir nur das BIRT Report Design-Plugin installieren , das eine vordefinierte Perspektive und den visuellen Editor enthält.

Für Entwickler, die Eclipse derzeit nicht verwenden und nicht wechseln möchten, gibt es ein Eclipse Report Designer-Paket , das aus einer tragbaren Eclipse-Installation mit vorinstalliertem BIRT-Plugin besteht.

Sobald die Berichtsdatei fertiggestellt ist, können wir sie in unserem Projekt speichern und zur Codierung in unserer bevorzugten Umgebung zurückkehren.

6. Der programmatische Ansatz

Wir können einen Bericht auch nur mit Code entwerfen , aber dieser Ansatz ist aufgrund der schlechten verfügbaren Dokumentation viel schwieriger. Seien Sie also bereit, sich mit Quellcode und Online-Foren zu befassen.

Erwägenswert ist auch, dass alle langwierigen Designdetails wie Größe, Länge und Rasterposition mit dem Designer viel einfacher zu handhaben sind .

Um diesen Punkt zu beweisen, finden Sie hier ein Beispiel für die Definition einer einfachen statischen Seite mit einem Bild und einem Text:

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!");

Dieser Code generiert einen einfachen (und hässlichen) Bericht:

Der im obigen Bild angezeigte Beispielbericht befindet sich unter folgendem Pfad: /reports/static_report.rptdesign.

Once we've coded how the report should look and what data it should display, we can generate the XML file by running our ReportDesignApplication class.

7. Attaching a Data Source

We mentioned earlier that BIRT supports many different data sources.

For our example project, we used a simple CSV file with three entries. It can be found in the reports folder and consists of three simple rows of data, plus headers:

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

7.1. Configuring the Data Source

To let BIRT use our file (or any other type of source), we have to configure a Data Source.

For our file, we created a Flat File Data Source with the report designer, all in just a few steps:

  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 diesem Artikel haben wir BIRT in Spring Boot integriert und dabei die Fallstricke und Herausforderungen, aber auch die Leistungsfähigkeit und Flexibilität untersucht.

Der Quellcode für den Artikel ist auf GitHub verfügbar.