我在运行带有 Oracle JRE (1.8u40) 的 Tomcat (8.0.21) 内使用 OpenSplice DDS (6.1.0p5,PrismTech 分布) 的 Web 应用程序时遇到困难。
背景
我们的代码使用 OpenSplice 库 dcpscj.jar、dcpssaj.jar、dlrlsaj.jar。出于许可和维护的原因,它们托管在外部目录 /opt/OpenSpliceDDS/V6.1.0p6/HDE/x86.linux2.6/jar 中,而不是嵌入到通常的 WEB-INF/lib 中的 WAR 文件中。
- 通过在可选的 bin/setenv.sh 文件中设置 CLASSPATH 变量,我已经成功地将这些可用到我们的 Web 应用程序中,按照包括 Tomcat ClassPath 中的外部 jar。
- 我还设置了 java.library.path 以使 JNI 部件工作,按照如何在 Tomcat 中添加本机库?.
设置环境文件
export CLASSPATH=/opt/OpenSpliceDDS/V6.1.0p6/HDE/x86.linux2.6/jar/dcpscj.jar:/opt/OpenSpliceDDS/V6.1.0p6/HDE/x86.linux2.6/jar/dcpssaj.jar:/opt/OpenSpliceDDS/V6.1.0p6/HDE/x86.linux2.6/jar/dlrlsaj.jar
export CATALINA_OPTS=-Djava.library.path=/opt/OpenSpliceDDS/V6.1.0p6/HDE/x86.linux2.6/lib
export LD_PRELOAD=/usr/java/jre/lib/i386/libjsig.so
根据tomcat 类加载文档,我还成功地通过 conf/catalina.properties 中的 common.loader 属性使库可用。
问题
使用 CLASSPATH 和 common.loader 方法时,Tomcat 在部署我们的 WAR 时始终会因 SIG_SEGV 而崩溃。
#
# A fatal error has been detected by the Java Runtime Environment:
#
# SIGSEGV (0xb) at pc=0x0142043a, pid=17613, tid=2004876144
#
# JRE version: Java(TM) SE Runtime Environment (8.0_40-b25) (build 1.8.0_40-b25)
# Java VM: Java HotSpot(TM) Server VM (25.40-b25 mixed mode linux-x86 )
# Problematic frame:
# V [libjvm.so+0x53543a] get_method_id(JNIEnv_*, _jclass*, char const*, char const*, bool, Thread*)+0x7a
#
# Core dump written. Default location: //core or core.17613
#
# If you would like to submit a bug report, please visit:
# http://bugreport.java.com/bugreport/crash.jsp
#
堆栈的顶部
V [libjvm.so+0x53543a] get_method_id(JNIEnv_*, _jclass*, char const*, char const*, bool, Thread*)+0x7a
V [libjvm.so+0x5467ad] jni_GetMethodID+0xbd
C [libdcpssaj.so+0x1569e] saj_cacheStructBuild+0x10e
C [libdcpssaj.so+0x148ae] saj_metaObject+0x9e
C [libdcpssaj.so+0x14b76] saj_copyCacheBuild+0x56
C [libdcpssaj.so+0x14c34] saj_copyCacheNew+0x94
C [libdcpssaj.so+0x29dcf] Java_org_opensplice_dds_dcps_FooTypeSupportImpl_jniRegisterType+0x21f
j org.opensplice.dds.dcps.FooTypeSupportImpl.jniRegisterType(Ljava/lang/Object;LDDS/DomainParticipant;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I+0
j org.opensplice.dds.dcps.FooTypeSupportImpl.registerType(Ljava/lang/Object;LDDS/DomainParticipant;Ljava/lang/String;)I+17
j org.example.dds.example_topic_typeTypeSupport.register_type(LDDS/DomainParticipant;Ljava/lang/String;)I+3
分析
仅当 JAR 在 WEB-INF/lib 外部时才会出现此问题,如果它们“嵌入”在 WEB-INF/lib 中,则 Tomcat 不会崩溃。
org.example.dds.example_topic_typeTypeSupport(匿名)是由 OpenSplice 生成的代码,我们将其打包为 WEB-INF/lib 中的单独 JAR。
example_topic_typeTypeSupport
调用FooTypeSupportImpl.registerType(),然后将类名作为 IDL 格式 "org::example::dds:example_topic_type" 的字符串传递到 JNI 部分saj_fooTypeSupport.c中。
这很难理解,但我相信最终 env->FindClass 会被 Java 变体调用,即 org.example.dds.example_topic_type。这似乎返回 NULL,然后将其传递给导致段错误的 jni_GetMethodID。
javaClass = (*(ctx->javaEnv))->FindClass (ctx->javaEnv, classDescriptor);
根据FindClass 文档,使用的类加载器是承载本机方法的类加载器。
FindClass 定位与当前本地方法关联的类加载器;即声明本机方法的类的类加载器。如果本地方法属于系统类,则不涉及类加载器
这意味着类加载器是用于加载位于 dcpssaj.jar 中的 FooTypeSupportImpl 的类加载器。这个类加载器看不到我们在 WEB-INF/lib/topics.jar 中的主题定义。
Tomcat类加载文档描述了每个模块的私有类加载器
Bootstrap | System <=== if dcpssaj.jar is loaded here then it can't see example_topic_type in topics.jar | Common <=== if dcpssaj.jar is loaded here then it can't see example_topic_type in topics.jar / Webapp1 <=== WEB-INF/lib/topics.jar containing example_topic_type
问题
- 无论如何在 Tomcat 中包含额外的 JAR 文件并让它们由用于在 WEB-INF/lib 中加载其他 JAR的相同类加载器加载?我正在寻找基于干净配置的解决方案 - 我已经考虑过解决方法,包括符号链接或在部署时通过一些脚本将 DDS JAR 移植到 WAR 文件中。
- 无论如何配置 OpenSplice DDS 来避免这个问题?