Spring Boot를 사용한 BIRT보고

1. 소개

이 자습서에서는 BIRT (비즈니스 인텔리전스 및보고 도구)를 Spring Boot MVC와 통합하여 HTML 및 PDF 형식의 정적 및 동적 보고서를 제공합니다.

2. BIRT 란 무엇입니까 ?

BIRT는 Java 웹 애플리케이션에 통합 할 수있는 데이터 시각화를 생성하는 오픈 소스 엔진 입니다.

Eclipse Foundation 내의 최상위 소프트웨어 프로젝트이며 IBM 및 Innovent Solutions의 기여를 활용합니다. 2004 년 말 Actuate에서 시작하고 후원했습니다.

이 프레임 워크를 사용하면 다양한 데이터 소스와 통합 된 보고서를 만들 수 있습니다.

3. Maven 종속성

BIRT에는 보고서 디자인 파일을 생성하는 시각적 보고서 디자이너 와 이러한 디자인을 해석하고 렌더링하기위한 런타임 구성 요소의 두 가지 주요 구성 요소가 있습니다.

예제 웹 애플리케이션에서는 Spring Boot 위에서 두 가지를 모두 사용할 것입니다.

3.1. BIRT 프레임 워크 종속성

종속성 관리 측면에서 생각했듯이 첫 번째 선택은 Maven Central에서 BIRT를 찾는 것입니다.

그러나 사용 가능한 최신 공식 버전의 코어 라이브러리는 2016 년부터 4.6이며 Eclipse 다운로드 페이지에서 적어도 두 가지 최신 버전 ( 현재는 4.8 )에 대한 링크를 찾을 수 있습니다 .

공식 빌드를 선택하는 경우 코드를 실행하고 실행하는 가장 쉬운 방법은 학습에도 유용한 완전한 웹 애플리케이션 인 BIRT 보고서 엔진 패키지를 다운로드하는 것입니다. 그런 다음 lib 폴더를 프로젝트 (약 68MB 크기) 에 복사하고 IDE에 모든 jar를 포함하도록 지시해야합니다.

이 접근 방식을 사용하면 로컬 리포지토리에서 수동으로 구성하고 설치하지 않는 한 Maven이 해당 jar를 찾을 수 없으므로 IDE를 통해서만 컴파일 할 수 있습니다 .

다행히 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. 스프링 부트 종속성

이제 BIRT를 프로젝트로 가져 왔으므로 pom 파일에 표준 Spring Boot 종속성을 추가하기 만하면됩니다.

하지만 한 가지 함정이 있습니다. BIRT jar에는 자체 Slf4J 구현이 포함되어 있기 때문에 Logback 과 잘 작동하지 않고 시작 중에 충돌 예외가 발생합니다.

jar에서 제거 할 수 없으므로이 문제를 해결하려면 Logback을 제외해야합니다 .

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

이제 드디어 시작할 준비가되었습니다!

4. BIRT 보고서

BIRT 프레임 워크에서 보고서는 rptdesign 확장자로 식별되는 긴 XML 구성 파일 입니다.

제목 스타일부터 데이터 소스에 연결하는 데 필요한 속성까지 무엇을 그리고 어디에 그릴 것인지 엔진에 알려줍니다 .

기본 동적 보고서의 경우 다음 세 가지를 구성해야합니다.

  1. 데이터 소스 (이 예에서는 로컬 CSV 파일을 사용하지만 쉽게 데이터베이스 테이블이 될 수 있음)
  2. 표시하려는 요소 (차트, 표 등)
  3. 페이지 디자인

보고서는 헤더, 본문, 바닥 글, 스크립트 및 스타일이 포함 된 HTML 페이지와 같은 구조입니다.

프레임 워크는 메인 스트림 데이터 소스, 레이아웃, 차트 및 테이블에 대한 통합을 포함하여 즉시 사용할 수있는 광범위한 구성 요소 세트를 제공합니다 . 그리고 그것을 확장하여 우리 자신을 추가 할 수 있습니다!

보고서 파일을 생성하는 방법에는 시각적 또는 프로그래밍 방식의 두 가지가 있습니다.

5. Eclipse 보고서 디자이너

보고서 작성을 용이하게하기 위해 Eclipse 팀 은 널리 사용되는 IDE 용 보고서 디자인 도구 플러그인을 구축했습니다 .

이 도구 는 왼쪽 팔레트 에서 쉽게 끌어서 놓기 인터페이스 를 제공하여 페이지에 추가하는 새 구성 요소에 대한 설정 창을 자동으로 엽니 다. 페이지에서 각 구성 요소를 클릭 한 다음 속성 편집기 버튼 (아래 이미지에서 강조 표시됨) 을 클릭하여 각 구성 요소에 대해 사용 가능한 모든 사용자 정의를 볼 수도 있습니다 .

전체 페이지 구조를 트리보기로 시각화하려면 개요 버튼 을 클릭하기 만하면 됩니다.

데이터 탐색기 탭은 또한 우리의 보고서에 정의 된 데이터 소스를 포함 :

이미지에 표시된 샘플 보고서는 /reports/csv_data_report.rptdesign 경로에서 찾을 수 있습니다.

비주얼 디자이너의 또 다른 장점은 프로그래밍 방식 대신이 도구에 더 중점을 둔 온라인 설명서입니다.

이미 Eclipse를 사용하고 있다면 미리 정의 된 Perspective 및 시각적 편집기가 포함 된 BIRT Report Design 플러그인을 설치하기 만하면 됩니다.

현재 Eclipse 를 사용하지 않고 전환을 원하지 않는 개발자를 위해 BIRT 플러그인이 사전 설치된 이식 가능한 Eclipse 설치로 구성된 Eclipse Report Designer 패키지 가 있습니다.

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에서 사용할 수 있습니다.