You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Gradle Shadow打包BIRT Report Engine为单可执行Jar时的空指针异常及报表运行问题排查求助

Gradle Shadow打包BIRT Report Engine为单可执行Jar时的空指针异常及报表运行问题排查求助

大家好,我现在正尝试用Java程序调用BIRT的Report Engine API来运行并渲染报表,使用的是Gradle 8.5和JDK 21构建运行程序。

用Gradle的run任务执行程序、生成报表都没问题,但打包成单个可执行Jar文件时遇到了麻烦。

问题重现:初始化Report Engine时的空指针异常

我的Report Engine初始化代码如下:

EngineConfig engineConfig = new EngineConfig();

engineConfig.setResourcePath(getConfigValue("resource_path")); // 返回报表资源文件夹路径

Platform.startup(engineConfig);

// 这个方法返回NULL
IReportEngineFactory engineFactory = (IReportEngineFactory) Platform
    .createFactoryObject(IReportEngineFactory.EXTENSION_REPORT_ENGINE_FACTORY);

// 这里抛出空指针异常
// java.lang.NullPointerException: Cannot invoke "org.eclipse.birt.report.engine.api.IReportEngineFactory.createReportEngine(org.eclipse.birt.report.engine.api.EngineConfig)" because "engineFactory" is null

engine = engineFactory.createReportEngine(engineConfig);

我的build.gradle配置:

plugins {
    id 'com.github.johnrengelman.shadow' version '8.1.1'
    id 'application'
}

version = '1.0.0'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.slf4j:slf4j-simple:2.0.9'
    implementation 'com.rabbitmq:amqp-client:5.20.0'
    implementation 'com.fasterxml.jackson.core:jackson-databind:2.16.0'
    implementation 'info.picocli:picocli:4.7.5'
    implementation fileTree('lib') { include '*.jar' }
}

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}

application {
    mainClass = 'report.engine.Main'
}

shadowJar {
    zip64 true
    archiveClassifier.set('standalone')
}

lib文件夹里放的是birt-runtime-4.14.0/ReportEngine/lib下的所有Jar包,shadowJar任务能正常完成并生成包含所有依赖的Fat Jar,但用java -jar "/path/to/fat.jar"运行时就会抛出上面的空指针异常,程序执行失败。

后续测试发现的现象

经过进一步测试,我发现如果把birt-runtime-4.14.0/ReportEngine/lib里的org.eclipse.birt.runtime_4.14.0-202312020807.jar放到Fat Jar所在的文件夹,再用同样的命令运行,createFactoryObject方法就不会返回null了,也不会触发之前的空指针。

但这时候尝试运行渲染报表(在正常用run任务、依赖文件夹在类路径下时能正常运行),又会抛出另一个异常,日志如下:

2023-12-28 07:40:52 | [FINE]    EngineTask.setParameterValue: theme=1_default [java.lang.String]
2023-12-28 07:40:52 | [FINE]    EngineTask.setParameterValue: ids=[Ljava.lang.Object;@669ed014 [[Ljava.lang.Object;]
2023-12-28 07:40:53 | [FINE]    Running the report with paramters: theme:1_default
ids:[Ljava.lang.Object;@669ed014
2023-12-28 07:40:53 | [FINE]    handle type: org.eclipse.birt.report.model.api.OdaDataSetHandle
2023-12-28 07:40:53 | [FINE]    Start to prepare a PreparedQuery.
2023-12-28 07:40:53 | [FINE]    Finished preparing the PreparedQuery.
2023-12-28 07:40:53 | [FINE]    Finished preparing query.
2023-12-28 07:40:53 | [SEVERE]  An error happened while running the report. Cause:
2023-12-28 07:40:53 | [SEVERE]  org.eclipse.birt.report.engine.api.EngineException: Error happened while running the report.
at org.eclipse.birt.report.engine.api.impl.EngineTask.handleFatalExceptions(EngineTask.java:1867)
at org.eclipse.birt.report.engine.api.impl.RunAndRenderTask.doRun(RunAndRenderTask.java:153)
at org.eclipse.birt.report.engine.api.impl.RunAndRenderTask.run(RunAndRenderTask.java:69)
at ivertix.bi.report.engine.ReportTask.run(ReportTask.java:73)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.lang.NullPointerException: Cannot read the array length because "extensions" is null
at org.eclipse.datatools.connectivity.oda.spec.manifest.ResultExtensionExplorer.addAllExtensions(ResultExtensionExplorer.java:541)
at org.eclipse.datatools.connectivity.oda.spec.manifest.ResultExtensionExplorer.<init>(ResultExtensionExplorer.java:122)
at org.eclipse.datatools.connectivity.oda.spec.manifest.ResultExtensionExplorer.getInstance(ResultExtensionExplorer.java:86)
at org.eclipse.birt.data.engine.impl.DataEngineImpl.getValidationContext(DataEngineImpl.java:681)
at org.eclipse.birt.data.engine.impl.OdaDataSetRuntime.<init>(OdaDataSetRuntime.java:54)
at org.eclipse.birt.data.engine.impl.DataSetRuntime.newInstance(DataSetRuntime.java:356)
at org.eclipse.birt.data.engine.impl.PreparedDataSourceQuery.configureDataSetCache(PreparedDataSourceQuery.java:198)
at org.eclipse.birt.data.engine.impl.PreparedDataSourceQuery.execute(PreparedDataSourceQuery.java:149)
at org.eclipse.birt.data.engine.impl.PreparedOdaDSQuery.execute(PreparedOdaDSQuery.java:172)
at org.eclipse.birt.report.data.adapter.impl.DataRequestSessionImpl.execute(DataRequestSessionImpl.java:594)
at org.eclipse.birt.report.engine.data.dte.DteDataEngine.doExecuteQuery(DteDataEngine.java:138)
at org.eclipse.birt.report.engine.data.dte.AbstractDataEngine.execute(AbstractDataEngine.java:254)
at org.eclipse.birt.report.engine.executor.ExecutionContext.executeQuery(ExecutionContext.java:1624)
at org.eclipse.birt.report.engine.executor.QueryItemExecutor.executeQuery(QueryItemExecutor.java:73)
at org.eclipse.birt.report.engine.executor.TableItemExecutor.execute(TableItemExecutor.java:60)
at org.eclipse.birt.report.engine.internal.executor.dup.SuppressDuplicateItemExecutor.execute(SuppressDuplicateItemExecutor.java:41)
at org.eclipse.birt.report.engine.internal.executor.wrap.WrappedReportItemExecutor.execute(WrappedReportItemExecutor.java:45)
at org.eclipse.birt.report.engine.internal.executor.l18n.LocalizedReportItemExecutor.execute(LocalizedReportItemExecutor.java:34)
at org.eclipse.birt.report.engine.layout.html.HTMLBlockStackingLM.layoutNodes(HTMLBlockStackingLM.java:62)
at org.eclipse.birt.report.engine.layout.html.HTMLPageLM.layout(HTMLPageLM.java:92)
at org.eclipse.birt.report.engine.layout.html.HTMLReportLayoutEngine.layout(HTMLReportLayoutEngine.java:97)
at org.eclipse.birt.report.engine.api.impl.RunAndRenderTask.doRun(RunAndRenderTask.java:145)
... 5 more

看起来问题根源在于BIRT Runtime的Jar包是被打包在Fat Jar内部,而不是和Fat Jar放在同一文件夹下。因为如果我把ReportEngine/lib下的所有Jar包都放到Fat Jar所在文件夹,再用java -jar "/path/to/fat.jar"运行,就不会有任何错误,报表能正常渲染。

这里有两点需要注意:

  • 使用java -jar命令时会忽略系统的classpath
  • Fat Jar所在文件夹里并没有其他非BIRT的外部依赖(比如slf4j、jackson、rabbitmq这些),它们只在Fat Jar内部,但运行完全正常

我觉得很奇怪,为什么BIRT Runtime的依赖和其他普通依赖表现不一样?

我们的目标是生成单个可执行Jar文件,这样作为服务部署时只需要上传这一个文件就能运行,不用额外操作——这应该是很常规的做法,但BIRT Runtime好像没法这么用?我不是Java专家,可能漏看了什么关键点,有没有大佬能指点一下?

备注:内容来源于stack exchange,提问作者dariobella

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.20 12:10:29