SpringBootを使用したBIRTレポート

1.はじめに

このチュートリアルでは、BIRT(ビジネスインテリジェンスおよびレポートツール)をSpring Boot MVCと統合して、静的および動的なレポートをHTMLおよびPDF形式で提供します。

2. BIRTとは何ですか?

BIRTは、JavaWebアプリケーションに統合できるデータ視覚化を作成するためのオープンソースエンジンです。

これはEclipseFoundation内のトップレベルのソフトウェアプロジェクトであり、IBMとInnoventSolutionsによる貢献を活用しています。これは、2004年の終わりにActuateによって開始され、後援されました。

このフレームワークにより、さまざまなデータソースと統合されたレポートを作成できます。

3.Mavenの依存関係

BIRTには、レポートデザインファイルを作成するためのビジュアルレポートデザイナーと、それらのデザインを解釈およびレンダリングするためのランタイムコンポーネントの2つの主要コンポーネントがあります。

この例のWebアプリケーションでは、SpringBootの上で両方を使用します。

3.1。BIRTフレームワークの依存関係

依存関係管理の観点から考えることに慣れているため、最初の選択肢はMavenCentralでBIRTを探すことです。

ただし、利用可能なコアライブラリの最新の公式バージョンは2016年から4.6ですが、Eclipseのダウンロードページには、少なくとも2つの新しいバージョン(現在は4.8)へのリンクがあります。

公式ビルドを選択した場合、コードを起動して実行する最も簡単な方法は、BIRT Report Engineパッケージをダウンロードすることです。これは、学習にも役立つ完全なWebアプリケーションです。次に、libフォルダーをプロジェクト(サイズは約68MB)にコピーし、IDEにすべてのjarファイルを含めるように指示する必要があります。

言うまでもなく、このアプローチを使用すると、IDEを介してのみコンパイルできます。ローカルリポジトリに手動で(100ファイル以上!)構成してインストールしない限り、Mavenはこれらのjarを検出しないためです。

幸い、Innovent Solutionsは問題を処理することを決定し、Maven Centralで最新のBIRT依存関係の独自のビルド公開しました。これは、必要なすべての依存関係を管理するので素晴らしいことです。

オンラインフォーラムのコメントを読むと、これらのアーティファクトが本番環境に対応しているかどうかは不明ですが、Innovent Solutionsは開始以来、Eclipseチームの隣のプロジェクトに取り組んでいたため、プロジェクトはそれらに依存しています。

BIRTを含めるのは非常に簡単になりました。

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

3.2。SpringBootの依存関係

BIRTがプロジェクトにインポートされたので、pomファイルに標準のSpringBoot依存関係を追加する必要があります。

ただし、BIRT jarにはSlf4Jの独自の実装が含まれているため、落とし穴が1つあります。これは、Logbackうまく連携せず、起動時に競合例外をスローします。

jarから削除できないため、この問題を修正するには、Logbackを除外する必要があります

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

これで、ようやく開始する準備が整いました。

4.BIRTレポート

BIRTフレームワークでは、レポートは長いXML構成ファイルであり、拡張子rptdesignで識別されます

タイトルのスタイルからデータソースに接続するために必要なプロパティまで、何をどこに描画するかをエンジンに指示します。

基本的な動的レポートの場合、次の3つを構成する必要があります。

  1. データソース(この例ではローカルCSVファイルを使用していますが、データベーステーブルである可能性があります)
  2. 表示したい要素(グラフ、表など)
  3. ページデザイン

レポートは、ヘッダー、本文、フッター、スクリプト、およびスタイルを備えたHTMLページのように構成されています。

フレームワークは、主流のデータソース、レイアウト、チャート、およびテーブルへの統合を含む、すぐに使用できるコンポーネントの広範なセットを提供します。そして、それを拡張して独自のものを追加することができます!

レポートファイルを生成するには、視覚的またはプログラム的な2つの方法があります。

5.Eclipseレポートデザイナー

レポートの作成を容易にするために、Eclipseチームは人気のあるIDE用のレポートデザインツールプラグインを作成しました。

このツールは、左側のパレットからの簡単なドラッグアンドドロップインターフェイスを備えており、ページに追加する新しいコンポーネントのセットアップウィンドウを自動的に開きます。また、ページ上でコンポーネントをクリックしてから、[プロパティエディタ]ボタン(下の画像で強調表示されている)をクリックすると、各コンポーネントで利用可能なすべてのカスタマイズを確認できます。

ページ構造全体をツリービューで視覚化するには、[アウトライン]ボタンをクリックするだけです。

[データエクスプローラー]タブには、レポート用に定義されたデータソースも含まれています。

画像に表示されているサンプルレポートは、パス/reports/csv_data_report.rptdesignにあります

ビジュアルデザイナーを選ぶもう1つの利点は、プログラムによるアプローチではなく、このツールに重点を置いたオンラインドキュメントです。

すでにEclipseを使用している場合は、事前定義されたパースペクティブとビジュアルエディターを含むBIRT ReportDesignプラグインをインストールする必要があります。

現在Eclipse使用しておらず、切り替えたくない開発者向けに、Eclipse Report Designerパッケージがあります。これは、BIRTプラグインがプリインストールされたポータブルEclipseインストールで構成されています。

Once the report file is finalized, we can save it in our project and go back to coding in our preferred environment.

6. The Programmatic Approach

We can also design a report using only code, but this approach is a lot harder due to the poor documentation available, so be prepared to dig into source code and online forums.

Also worth considering is that all the tedious design details like size, length, and grid position are a lot easier to deal with using the designer.

To prove this point, here's an example of how to define a simple static page with an image and a 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!");

This code will generate a simple (and ugly) report:

The sample report displayed in the image above can be found at this path: /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

この記事では、BIRTをSpring Bootと統合し、落とし穴と課題だけでなく、そのパワーと柔軟性についても説明しました。

この記事のソースコードはGitHubで入手できます。