切换Maven后JODConverter抛出UnsatisfiedLinkError的原因排查
我有一个基于Eclipse的Java项目,原本依赖多个库但无法构建,于是改成Maven管理来实现依赖自动更新。现在项目构建正常,但JODConverter运行时找不到本地库,报错如下:
Exception in thread "jodconverter-offprocmng-0" java.lang.UnsatisfiedLinkError: no jpipe in java.library.path: [/usr/java/packages/lib, /usr/lib/x86_64-linux-gnu/jni, /lib/x86_64-linux-gnu, /usr/lib/x86_64-linux-gnu, /usr/lib/jni, /lib, /usr/lib]
at java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2678)
指定以下参数可以临时解决问题:
-Djava.library.path="/usr/lib/libreoffice/program/"
但奇怪的是,同一系统上仅包含JODConverter的简单测试项目,使用完全相同的java.library.path(/usr/java/packages/lib:/usr/lib/x86_64-linux-gnu/jni:/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu:/usr/lib/jni:/lib:/usr/lib)却能正常找到jpipe库。
补充启动命令行信息
正常测试项目的启动命令行
/usr/lib/jvm/java-11-openjdk-amd64/bin/java -Dfile.encoding=UTF-8 -Dstdout.encoding=UTF-8 -Dstderr.encoding=UTF-8 -classpath /home/rydell/eclipse-workspace/JODConverterTests2024/target/classes:/home/rydell/.m2/repository/org/jodconverter/jodconverter-local/4.4.7/jodconverter-local-4.4.7.jar:/home/rydell/.m2/repository/org/jodconverter/jodconverter-core/4.4.7/jodconverter-core-4.4.7.jar:/home/rydell/.m2/repository/com/google/code/gson/gson/2.9.1/gson-2.9.1.jar:/home/rydell/.m2/repository/org/openoffice/juh/4.1.2/juh-4.1.2.jar:/home/rydell/.m2/repository/org/openoffice/jurt/4.1.2/jurt-4.1.2.jar:/home/rydell/.m2/repository/org/openoffice/ridl/4.1.2/ridl-4.1.2.jar:/home/rydell/.m2/repository/org/openoffice/unoil/4.1.2/unoil-4.1.2.jar:/home/rydell/.m2/repository/org/slf4j/slf4j-api/2.0.11/slf4j-api-2.0.11.jar:/home/rydell/.m2/repository/org/slf4j/slf4j-nop/2.0.11/slf4j-nop-2.0.11.jar:/home/rydell/.m2/repository/net/lingala/zip4j/zip4j/2.11.5/zip4j-2.11.5.jar jodtest.JodTestMain
异常项目的启动命令行(需指定java.library.path才能运行)
/usr/lib/jvm/java-11-openjdk-amd64/bin/java -Djava.library.path=/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib:/usr/lib/libreoffice/program/ -Dfile.encoding=UTF-8 -Dstdout.encoding=UTF-8 -Dstderr.encoding=UTF-8 -classpath /home/rydell/tests/PackingServer_temp/target/classes:/home/rydell/.m2/repository/org/jodconverter/jodconverter-local/4.4.7/jodconverter-local-4.4.7.jar:/home/rydell/.m2/repository/org/jodconverter/jodconverter-core/4.4.7/jodconverter-core-4.4.7.jar:/home/rydell/.m2/repository/com/google/code/gson/gson/2.9.1/gson-2.9.1.jar:/home/rydell/.m2/repository/org/openoffice/juh/4.1.2/juh-4.1.2.jar:/home/rydell/.m2/repository/org/openoffice/jurt/4.1.2/jurt-4.1.2.jar:/home/rydell/.m2/repository/org/openoffice/ridl/4.1.2/ridl-4.1.2.jar:/home/rydell/.m2/repository/org/openoffice/unoil/4.1.2/unoil-4.1.2.jar:/home/rydell/.m2/repository/org/slf4j/slf4j-api/2.0.11/slf4j-api-2.0.11.jar:/home/rydell/.m2/repository/org/slf4j/slf4j-simple/2.0.11/slf4j-simple-2.0.11.jar:/home/rydell/.m2/repository/net/lingala/zip4j/zip4j/2.11.5/zip4j-2.11.5.jar:/home/rydell/.m2/repository/de/idyl/winzipaes/1.0.1/winzipaes-1.0.1.jar:/home/rydell/.m2/repository/org/bouncycastle/bcprov-jdk16/1.45/bcprov-jdk16-1.45.jar:/home/rydell/.m2/repository/org/apache/pdfbox/pdfbox/3.0.1/pdfbox-3.0.1.jar:/home/rydell/.m2/repository/org/apache/pdfbox/pdfbox-io/3.0.1/pdfbox-io-3.0.1.jar:/home/rydell/.m2/repository/org/apache/pdfbox/fontbox/3.0.1/fontbox-3.0.1.jar:/home/rydell/.m2/repository/commons-logging/commons-logging/1.2/commons-logging-1.2.jar:/home/rydell/.m2/repository/org/junit/jupiter/junit-jupiter/5.10.1/junit-jupiter-5.10.1.jar:/home/rydell/.m2/repository/org/junit/jupiter/junit-jupiter-api/5.10.1/junit-jupiter-api-5.10.1.jar:/home/rydell/.m2/repository/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar:/home/rydell/.m2/repository/org/junit/platform/junit-platform-commons/1.10.1/junit-platform-commons-1.10.1.jar:/home/rydell/.m2/repository/org/apiguardian/apiguardian-api/1.1.2/apiguardian-api-1.1.2.jar:/home/rydell/.m2/repository/org/junit/jupiter/junit-jupiter-params/5.10.1/junit-jupiter-params-5.10.1.jar:/home/rydell/.m2/repository/org/junit/jupiter/junit-jupiter-engine/5.10.1/junit-jupiter-engine-5.10.1.jar:/home/rydell/.m2/repository/org/junit/platform/junit-platform-engine/1.10.1/junit-platform-engine-1.10.1.jar:/home/rydell/.m2/repository/org/mariadb/jdbc/mariadb-java-client/3.3.2/mariadb-java-client-3.3.2.jar:/home/rydell/.m2/repository/com/github/waffle/waffle-jna/3.3.0/waffle-jna-3.3.0.jar:/home/rydell/.m2/repository/net/java/dev/jna/jna/5.13.0/jna-5.13.0.jar:/home/rydell/.m2/repository/net/java/dev/jna/jna-platform/5.13.0/jna-platform-5.13.0.jar:/home/rydell/.m2/repository/org/slf4j/jcl-over-slf4j/2.0.7/jcl-over-slf4j-2.0.7.jar:/home/rydell/.m2/repository/com/github/ben-manes/caffeine/caffeine/2.9.3/caffeine-2.9.3.jar:/home/rydell/.m2/repository/com/google/errorprone/error_prone_annotations/2.10.0/error_prone_annotations-2.10.0.jar:/home/rydell/.m2/repository/org/checkerframework/checker-qual/3.32.0/checker-qual-3.32.0.jar:/home/rydell/.m2/repository/com/sun/mail/javax.mail/1.6.2/javax.mail-1.6.2.jar:/home/rydell/.m2/repository/javax/activation/activation/1.1.1/activation-1.1.1.jar:/home/rydell/.m2/repository/org/apache/commons/commons-lang3/3.14.0/commons-lang3-3.14.0.jar:/home/rydell/.m2/repository/commons-io/commons-io/2.15.1/commons-io-2.15.1.jar:/home/rydell/.m2/repository/commons-cli/commons-cli/1.6.0/commons-cli-1.6.0.jar:/home/rydell/.m2/repository/org/bouncycastle/bcprov-jdk15on/1.70/bcprov-jdk15on-1.70.jar:/home/rydell/.m2/repository/net/sourceforge/barbecue/barbecue/1.5-beta1/barbecue-1.5-beta1.jar parser.CODA --rowDelimiter # --fieldDelimiter ; --outputFilenameTemplate ECLIPSE_WORKAROUND_FILENAMETEMPLATE2 /home/rydell/TODO_TODAY_TOMORROW/temp/whatever.odt
问题根源分析
对比两个项目的参数与依赖,核心差异点如下:
- 日志框架差异:测试项目用
slf4j-nop,异常项目用slf4j-simple,日志框架初始化时机可能干扰JODConverter的库加载流程。 - 依赖数量与冲突:异常项目包含大量额外依赖(如PDFBox、JNA、JUnit等),这些依赖可能改变类加载器优先级,导致JODConverter相关类被非系统类加载器加载,无法读取默认路径下的本地库。
- JNA的干扰:异常项目依赖JNA,JNA会修改系统本地库搜索路径,可能与JODConverter的库加载逻辑冲突。
修复方案
方案1:调整依赖加载顺序
在项目pom.xml中,将JODConverter相关依赖(jodconverter-local、jodconverter-core、OpenOffice系列依赖)移至依赖列表最顶部,确保其优先被加载,避免其他依赖干扰类加载流程。
方案2:解决依赖冲突
- 统一日志框架:确保
commons-logging与jcl-over-slf4j的桥接配置正确,避免日志类加载冲突。 - 移除不必要依赖:若项目无需JNA,可暂时移除该依赖,验证是否解决问题;若必须保留,检查JNA版本与JODConverter的兼容性。
方案3:代码动态添加库路径
在项目启动最早期,通过反射动态将目标路径添加到系统库搜索路径:
import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; public class LibraryPathUtil { public static void addLibraryPath(String path) throws Exception { Field field = ClassLoader.class.getDeclaredField("usr_paths"); field.setAccessible(true); String[] paths = (String[]) field.get(null); List<String> pathList = new ArrayList<>(List.of(paths)); if (!pathList.contains(path)) { pathList.add(path); field.set(null, pathList.toArray(new String[0])); } } }
启动时调用LibraryPathUtil.addLibraryPath("/usr/lib/libreoffice/program/");。
方案4:配置系统环境变量
将jpipe所在目录添加到系统LD_LIBRARY_PATH环境变量,Java进程会自动继承该路径:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/lib/libreoffice/program/
内容的提问来源于stack exchange,提问作者ospf

