2

我在运行带有 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 文件中。

设置环境文件

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 来避免这个问题?
4

0 回答 0