BIRT在春季启动应用程序

我需要在现有的Spring启动Web应用程序中创建报告function(function)。 建议是使用BIRT,我可以将其与spring boot web app集成。

我找到了下面的文章,并且能够在spring boot starter项目中运行报告(使用http://start.spring.io/ )。 这篇相当陈旧的文章确实帮助我找到了一个有效的例子。 https://spring.io/blog/2012/01/30/spring-framework-birt 。 这篇文章基本上就是我想要的,但是在一个spring boot web app中。

我面临的挑战是使用BIRT查看器运行报告,该查看器具有很好的附加function。 (打印,Expoet数据,PDF,分页等)

我没有像本文所描述的那样使用BIRT找到任何弹簧启动示例。

我的问题是:

  1. 是否有其他方法或其他方式在Spring启动Web应用程序中执行报告? (显然不想通过从头开始创建类似BIRT的function来重新发明轮子,或者如果可能的话,将报告引擎与Web应用程序分开运行)

  2. 今天有没有人在Spring Boot Web应用程序中使用BIRT(与观众一起工作),并愿意分享或教育我最好的方法吗? (我试图让JSP页面与spring boot一起工作,但是无法成功…更缺乏经验而不是其他任何东西)

有谁可以帮助我吗。

亲切的问候,Henk

依赖关系, IReportEngine的实例和目录是在Spring启动Web应用程序中设置BIRT最具挑战性的部分。 此示例有一点error handling,并依赖于环境变量来指定重要目录的路径。

这个设置的一个好处是它是一个独立的ReST API,用于构建和生成报告。 作为附带好处,它根本不依赖于JSP。

我在Docker容器中运行它。

最初有一个计划使用一些接口来尝试其他报告框架,如Jasper报告或其他。

这并不是你要求的,尽管在我看来它在某些情况下更好。 使用,配置和部署BIRT报告运行器作为这样的应用程序,您有很大的灵活性。 这不使用Actian提供的预先打包的BIRT Viewer。

我会为名为birt-report-runner-data的报告创建一个数据容器,然后将所有目录用于记录和报告该容器,然后将其安装到BIRT容器上。

主要成分

主文件(BirtReportRunnerApplication.java)

 package com.example; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class BirtReportRunnerApplication { public static void main(String[] args) { SpringApplication.run(BirtReportRunnerApplication.class, args); } } 

控制器接收报告请求(ReportController.java)

 package com.example.core.web.controller; import com.example.core.model.BIRTReport; import com.example.core.service.ReportRunner; import com.example.core.web.dto.ReportRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; @RestController("ReportController") @RequestMapping("/reports") public class ReportController { private Logger logger = LoggerFactory.getLogger(ReportController.class); @Autowired @Qualifier("birt") ReportRunner reportRunner; @RequestMapping(value = "/birt", method = RequestMethod.POST) public ResponseEntity getBIRTReport(@RequestBody ReportRequest reportRequest) { byte[] reportBytes; ResponseEntity responseEntity; try { logger.info("REPORT REQUEST NAME: " + reportRequest.getReportName()); reportBytes = new BIRTReport( reportRequest.getReportName(), reportRequest.getReportParameters(), reportRunner) .runReport().getReportContent().toByteArray(); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setContentType(MediaType.parseMediaType("application/pdf")); String fileName = reportRequest.getReportName() + ".pdf"; httpHeaders.setContentDispositionFormData(fileName, fileName); httpHeaders.setCacheControl("must-revalidate, post-check=0, pre-check=0"); responseEntity = new ResponseEntity(reportBytes, httpHeaders, HttpStatus.OK); } catch (Exception e) { responseEntity = new ResponseEntity(HttpStatus.NOT_IMPLEMENTED); return responseEntity; } return responseEntity; } } 

报告请求DTO,用于指定要运行的报告(ReportRequest.java)

 package com.example.core.web.dto; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.List; public class ReportRequest { private String reportName; private String reportParameters; public ReportRequest(@JsonProperty("reportName") String reportName, @JsonProperty("reportParameters") String reportParameters) { this.reportName = reportName; this.reportParameters = reportParameters; } public String getReportName() { return reportName; } public String getReportParameters() { return reportParameters; } public void setReportParameters(String reportParameters) { this.reportParameters = reportParameters; } } 

报表运行器的接口(ReportRunner.java)

 package com.example.core.service; import com.example.core.model.Report; import java.io.ByteArrayOutputStream; public interface ReportRunner { ByteArrayOutputStream runReport(Report report); } 

完成大部分工作的最大类(BIRTReportRunner.java)

 package com.example.core.service; import com.example.core.model.Report; import org.eclipse.birt.core.exception.BirtException; import org.eclipse.birt.core.framework.Platform; import org.eclipse.birt.report.engine.api.*; import org.eclipse.core.internal.registry.RegistryProviderFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.core.env.Environment; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.io.ByteArrayOutputStream; import java.io.File; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; import java.util.logging.Level; @Service @Qualifier("birt") public class BIRTReportRunner implements ReportRunner { private static final String DEFAULT_LOGGING_DIRECTORY = "defaultBirtLoggingDirectory/"; private Logger logger = LoggerFactory.getLogger(BIRTReportRunner.class); private static String reportOutputDirectory; private IReportEngine birtReportEngine = null; @Autowired private Environment env; /** * Starts up and configures the BIRT Report Engine */ @PostConstruct public void startUp() { if(env.getProperty("birt_report_input_dir") == null) throw new RuntimeException("Cannot start application since birt report input directory was not specified."); try { String birtLoggingDirectory = env.getProperty("birt_logging_directory") == null ? DEFAULT_LOGGING_DIRECTORY : env.getProperty("birt_logging_directory"); Level birtLoggingLevel = env.getProperty("birt_logging_level") == null ? Level.SEVERE : Level.parse(env.getProperty("birt_logging_level")); EngineConfig engineConfig = new EngineConfig(); logger.info("BIRT LOG DIRECTORY SET TO : {}", birtLoggingDirectory); logger.info("BIRT LOGGING LEVEL SET TO {}", birtLoggingLevel); engineConfig.setLogConfig(birtLoggingDirectory, birtLoggingLevel); // Required due to a bug in BIRT that occurs in calling Startup after the Platform has already been started up RegistryProviderFactory.releaseDefault(); Platform.startup(engineConfig); IReportEngineFactory reportEngineFactory = (IReportEngineFactory) Platform.createFactoryObject(IReportEngineFactory.EXTENSION_REPORT_ENGINE_FACTORY); birtReportEngine = reportEngineFactory.createReportEngine(engineConfig); } catch (BirtException e) { // TODO add logging aspect and find out how to log a platform startup problem from this catch block, if possible, using the aspect. // Possibly rethrow the exception here and catch it in the aspect. logger.error("Birt Startup Error: {}", e.getMessage()); } reportOutputDirectory = env.getProperty("birt_temp_file_output_dir"); } /** * Shuts down the BIRT Report Engine */ @PreDestroy public void shutdown() { birtReportEngine.destroy(); RegistryProviderFactory.releaseDefault(); Platform.shutdown(); } public File getReportFromFilesystem(String reportName) throws RuntimeException { String reportDirectory = env.getProperty("birt_report_input_dir"); Path birtReport = Paths.get(reportDirectory + File.separator + reportName + ".rptdesign"); if(!Files.isReadable(birtReport)) throw new RuntimeException("Report " + reportName + " either did not exist or was not writable."); return birtReport.toFile(); } /** * This method creates and executes the report task, the main responsibility * of the entire Report Service. * This method is key to enabling pagination for the BIRT report. The IRunTask run task * is created and then used to generate an ".rptdocument" binary file. * This binary file is then read by the separately created IRenderTask render * task. The render task renders the binary document as a binary PDF output * stream which is then returned from the method. * 

* * @param birtReport the report object created at the controller to hold the data of the report request. * @return Returns a ByteArrayOutputStream of the PDF bytes generated by the */ @Override public ByteArrayOutputStream runReport(Report birtReport) { ByteArrayOutputStream byteArrayOutputStream; File rptDesignFile; // get the path to the report design file try { rptDesignFile = getReportFromFilesystem(birtReport.getName()); } catch (Exception e) { logger.error("Error while loading rptdesign: {}.", e.getMessage()); throw new RuntimeException("Could not find report"); } // process any additional parameters Map parsedParameters = parseParametersAsMap(birtReport.getParameters()); byteArrayOutputStream = new ByteArrayOutputStream(); try { IReportRunnable reportDesign = birtReportEngine.openReportDesign(rptDesignFile.getPath()); IRunTask runTask = birtReportEngine.createRunTask(reportDesign); if (parsedParameters.size() > 0) { for (Map.Entry entry : parsedParameters.entrySet()) { runTask.setParameterValue(entry.getKey(), entry.getValue()); } } runTask.validateParameters(); String rptdocument = reportOutputDirectory + File.separator + "generated" + File.separator + birtReport.getName() + ".rptdocument"; runTask.run(rptdocument); IReportDocument reportDocument = birtReportEngine.openReportDocument(rptdocument); IRenderTask renderTask = birtReportEngine.createRenderTask(reportDocument); PDFRenderOption pdfRenderOption = new PDFRenderOption(); pdfRenderOption.setOption(IPDFRenderOption.REPAGINATE_FOR_PDF, new Boolean(true)); pdfRenderOption.setOutputFormat("pdf"); pdfRenderOption.setOutputStream(byteArrayOutputStream); renderTask.setRenderOption(pdfRenderOption); renderTask.render(); renderTask.close(); } catch (EngineException e) { logger.error("Error while running report task: {}.", e.getMessage()); // TODO add custom message to thrown exception throw new RuntimeException(); } return byteArrayOutputStream; } /** * Takes a String of parameters started by '?', delimited by '&', and with * keys and values split by '=' and returnes a Map of the keys and values * in the String. * * @param reportParameters a String from a HTTP request URL * @return a map of parameters with Key,Value entries as strings */ public Map parseParametersAsMap(String reportParameters) { Map parsedParameters = new HashMap(); String[] paramArray; if (reportParameters.isEmpty()) { throw new IllegalArgumentException("Report parameters cannot be empty"); } else if (!reportParameters.startsWith("?") && !reportParameters.contains("?")) { throw new IllegalArgumentException("Report parameters must start with a question mark '?'!"); } else { String noQuestionMark = reportParameters.substring(1, reportParameters.length()); paramArray = noQuestionMark.split("&"); for (String param : paramArray) { String[] paramGroup = param.split("="); if (paramGroup.length == 2) { parsedParameters.put(paramGroup[0], paramGroup[1]); } else { parsedParameters.put(paramGroup[0], ""); } } } return parsedParameters; } }

报告对象类(Report.java)

 package com.example.core.model; import com.example.core.service.ReportRunner; import java.io.ByteArrayOutputStream; import java.util.List; /** * A Report object has a byte representation of the report output that can be * used to write to any output stream. This class is designed around the concept * of using ByteArrayOutputStreams to write PDFs to an output stream. * * */ public abstract class Report { protected String name; protected String parameters; protected ByteArrayOutputStream reportContent; protected ReportRunner reportRunner; public Report(String name, String parameters, ReportRunner reportRunner) { this.name = name; this.parameters = parameters; this.reportRunner = reportRunner; } /** * This is the processing method for a Report. Once the report is ran it * populates an internal field with a ByteArrayOutputStream of the * report content generated during the run process. * @return Returns itself with the report content output stream created. */ public abstract Report runReport(); public ByteArrayOutputStream getReportContent() { return this.reportContent; } public String getName() { return name; } public String getParameters() { return parameters; } } 

BIRTReport对象类(BIRTReport.java)

 package com.example.core.model; import com.example.core.service.ReportRunner; public class BIRTReport extends Report { public BIRTReport(String name, String reportParameters, ReportRunner reportRunner) { super(name, reportParameters, reportRunner); } @Override public Report runReport() { this.reportContent = reportRunner.runReport(this); return this; } } 

构建文件(build.gradle)

 buildscript { ext { springBootVersion = '1.3.2.RELEASE' } repositories { jcenter() } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") classpath("io.spring.gradle:dependency-management-plugin:0.5.2.RELEASE") } } apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'idea' apply plugin: 'spring-boot' jar { baseName = 'com.example.simple-birt-runner' version = '1.0.0' } sourceCompatibility = 1.8 targetCompatibility = 1.8 ext { springBootVersion = '1.3.0.M5' } repositories { jcenter() mavenCentral() } dependencies { compile("org.springframework.boot:spring-boot-starter-actuator:${springBootVersion}") compile("org.springframework.boot:spring-boot-starter-web:${springBootVersion}") // BIRT Runtime and ReportEngine dependencies // These were pulled from the Actuate download site at http://download.eclipse.org/birt/downloads/ for version 4.5.0 compile fileTree(dir: 'lib', include: '*.jar') /*compile("org.eclipse.birt.runtime:org.eclipse.birt.runtime:4.4.2") { exclude group: "org.eclipse.birt.runtime", module: "org.apache.poi" exclude group: "org.eclipse.birt.runtime", module: "org.eclipse.orbit.mongodb" }*/ compile("org.springframework.boot:spring-boot-starter-tomcat") // include any runtime JDBC driver dependencies here testCompile("org.springframework.boot:spring-boot-starter-test") } 

依赖

获取Birt Viewer lib目录中的所有依赖项并将它们放在类路径中。 以下是外部依赖项的完整列表:

 com.ibm.icu_54.1.1.v201501272100.jar com.lowagie.text_2.1.7.v201004222200.jar derby.jar flute.jar javax.wsdl_1.5.1.v201012040544.jar javax.xml.stream_1.0.1.v201004272200.jar javax.xml_1.3.4.v201005080400.jar net.sourceforge.lpg.lpgjavaruntime_1.1.0.v201004271650.jar org.apache.batik.bridge_1.6.0.v201011041432.jar org.apache.batik.css_1.6.0.v201011041432.jar org.apache.batik.dom.svg_1.6.0.v201011041432.jar org.apache.batik.dom_1.6.1.v201505192100.jar org.apache.batik.ext.awt_1.6.0.v201011041432.jar org.apache.batik.parser_1.6.0.v201011041432.jar org.apache.batik.pdf_1.6.0.v201105071520.jar org.apache.batik.svggen_1.6.0.v201011041432.jar org.apache.batik.transcoder_1.6.0.v201011041432.jar org.apache.batik.util.gui_1.6.0.v201011041432.jar org.apache.batik.util_1.6.0.v201011041432.jar org.apache.batik.xml_1.6.0.v201011041432.jar org.apache.commons.codec_1.6.0.v201305230611.jar org.apache.commons.logging_1.1.1.v201101211721.jar org.apache.lucene.core_3.5.0.v20120725-1805.jar org.apache.poi_3.9.0.v201405241750.jar org.apache.xerces_2.9.0.v201101211617.jar org.apache.xml.resolver_1.2.0.v201005080400.jar org.apache.xml.serializer_2.7.1.v201005080400.jar org.eclipse.birt.runtime_4.5.0.jar org.eclipse.core.contenttype_3.5.0.v20150421-2214.jar org.eclipse.core.expressions_3.5.0.v20150421-2214.jar org.eclipse.core.filesystem_1.5.0.v20150421-0713.jar org.eclipse.core.jobs_3.7.0.v20150330-2103.jar org.eclipse.core.resources_3.10.0.v20150423-0755.jar org.eclipse.core.runtime.compatibility_3.2.300.v20150423-0821.jar org.eclipse.core.runtime_3.11.0.v20150405-1723.jar org.eclipse.datatools.connectivity.apache.derby.dbdefinition_1.0.2.v201107221459.jar org.eclipse.datatools.connectivity.apache.derby_1.0.103.v201212070447.jar org.eclipse.datatools.connectivity.console.profile_1.0.10.v201109250955.jar org.eclipse.datatools.connectivity.db.generic_1.0.1.v201107221459.jar org.eclipse.datatools.connectivity.dbdefinition.genericJDBC_1.0.2.v201310181001.jar org.eclipse.datatools.connectivity.oda.consumer_3.2.6.v201403131814.jar org.eclipse.datatools.connectivity.oda.design_3.3.6.v201403131814.jar org.eclipse.datatools.connectivity.oda.flatfile_3.1.8.v201403010906.jar org.eclipse.datatools.connectivity.oda.profile_3.2.9.v201403131814.jar org.eclipse.datatools.connectivity.oda_3.4.3.v201405301249.jar org.eclipse.datatools.connectivity.sqm.core_1.2.8.v201401230755.jar org.eclipse.datatools.connectivity_1.2.11.v201401230755.jar org.eclipse.datatools.enablement.hsqldb.dbdefinition_1.0.0.v201107221502.jar org.eclipse.datatools.enablement.hsqldb_1.0.0.v201107221502.jar org.eclipse.datatools.enablement.ibm.db2.iseries.dbdefinition_1.0.3.v201107221502.jar org.eclipse.datatools.enablement.ibm.db2.iseries_1.0.2.v201107221502.jar org.eclipse.datatools.enablement.ibm.db2.luw.dbdefinition_1.0.7.v201405302027.jar org.eclipse.datatools.enablement.ibm.db2.luw_1.0.3.v201401170830.jar org.eclipse.datatools.enablement.ibm.db2.zseries.dbdefinition_1.0.4.v201107221502.jar org.eclipse.datatools.enablement.ibm.db2.zseries_1.0.2.v201107221502.jar org.eclipse.datatools.enablement.ibm.db2_1.0.0.v201401170830.jar org.eclipse.datatools.enablement.ibm.informix.dbdefinition_1.0.4.v201107221502.jar org.eclipse.datatools.enablement.ibm.informix_1.0.1.v201107221502.jar org.eclipse.datatools.enablement.ibm_1.0.0.v201401170830.jar org.eclipse.datatools.enablement.msft.sqlserver.dbdefinition_1.0.1.v201201240505.jar org.eclipse.datatools.enablement.msft.sqlserver_1.0.3.v201308161009.jar org.eclipse.datatools.enablement.mysql.dbdefinition_1.0.4.v201109022331.jar org.eclipse.datatools.enablement.mysql_1.0.4.v201212120617.jar org.eclipse.datatools.enablement.oda.ws_1.2.6.v201403131825.jar org.eclipse.datatools.enablement.oda.xml_1.2.5.v201403131825.jar org.eclipse.datatools.enablement.oracle.dbdefinition_1.0.103.v201206010214.jar org.eclipse.datatools.enablement.oracle_1.0.0.v201107221506.jar org.eclipse.datatools.enablement.postgresql.dbdefinition_1.0.2.v201110070445.jar org.eclipse.datatools.enablement.postgresql_1.1.1.v201205252207.jar org.eclipse.datatools.enablement.sap.maxdb.dbdefinition_1.0.0.v201107221507.jar org.eclipse.datatools.enablement.sap.maxdb_1.0.0.v201107221507.jar org.eclipse.datatools.modelbase.dbdefinition_1.0.2.v201107221519.jar org.eclipse.datatools.modelbase.derby_1.0.0.v201107221519.jar org.eclipse.datatools.modelbase.sql.query_1.1.4.v201212120619.jar org.eclipse.datatools.modelbase.sql_1.0.6.v201208230744.jar org.eclipse.datatools.sqltools.data.core_1.2.3.v201212120623.jar org.eclipse.datatools.sqltools.parsers.sql.lexer_1.0.1.v201107221520.jar org.eclipse.datatools.sqltools.parsers.sql.query_1.2.1.v201201250511.jar org.eclipse.datatools.sqltools.parsers.sql_1.0.2.v201107221520.jar org.eclipse.datatools.sqltools.result_1.1.6.v201402080246.jar org.eclipse.emf.common_2.11.0.v20150512-0501.jar org.eclipse.emf.ecore.change_2.11.0.v20150512-0501.jar org.eclipse.emf.ecore.xmi_2.11.0.v20150512-0501.jar org.eclipse.emf.ecore_2.11.0.v20150512-0501.jar org.eclipse.equinox.app_1.3.300.v20150423-1356.jar org.eclipse.equinox.common_3.7.0.v20150402-1709.jar org.eclipse.equinox.preferences_3.5.300.v20150408-1437.jar org.eclipse.equinox.registry_3.6.0.v20150318-1503.jar org.eclipse.help_3.6.0.v20130326-1254.jar org.eclipse.osgi.services_3.5.0.v20150519-2006.jar org.eclipse.osgi_3.10.100.v20150529-1857.jar org.eclipse.update.configurator_3.3.300.v20140518-1928.jar org.mozilla.javascript_1.7.5.v201504281450.jar org.w3c.css.sac_1.3.1.v200903091627.jar org.w3c.dom.events_3.0.0.draft20060413_v201105210656.jar org.w3c.dom.smil_1.0.1.v200903091627.jar org.w3c.dom.svg_1.1.0.v201011041433.jar 

应用程序属性(application.yml)

 birt: report: output: dir: ${birt_temp_file_output_dir} input: dir: ${birt_report_input_dir} logging: level: ${birt_logging_level} server: port: 8080 context-path: /simple-birt-runner birt: logging: level: SEVERE logging: level: org: springframework: web: ERROR 

用于运行的Dockerfile(Dockerfile)

 FROM java:openjdk-8u66-jre MAINTAINER Kent O. Johnson  COPY com.example.simple-birt-runner-*.jar /opt/soft/simple-birt-runner.jar RUN mkdir -p /reports/input \ && mkdir /reports/output \ && mkdir -p /reports/log/engine WORKDIR /opt/soft CMD java -jar -Xms128M -Xmx4G simple-birt-runner.jar EXPOSE 8080 

Docker撰写用于运行映像的文件(docker-compose.yml)

 simple-birt-runner: image: soft/simple-birt-runner-release ports: - "8090:8080" environment: - birt_temp_file_output_dir=/reports/output - birt_report_input_dir=/reports/input - birt_logging_directory=/reports/log/engine - birt_logging_level=SEVERE volumes_from: - birt-report-runner-data 

关于@Kent Johnson的回答。 我没有设法使用gradle配置项目,但我设法用maven构建它。 下面是pom.xml

    4.0.0 birt-runner com.example.simple-birt-runner 1.0-SNAPSHOT jar  org.springframework.boot spring-boot-starter-parent 1.4.4.RELEASE     UTF-8 UTF-8 1.8    org.springframework.boot spring-boot-starter-web   org.springframework.boot spring-boot-starter-tomcat    org.springframework.boot spring-boot-starter-test test    org.springframework spring-web    org.eclipse.birt.runtime org.eclipse.birt.runtime 4.2.0    org.springframework spring-core    com.fasterxml.jackson.core jackson-databind      org.springframework.boot spring-boot-maven-plugin