Ошибка Tomcat, когда код JNI использует env->FindClass() для загрузчиков классов
У меня возникают трудности при запуске веб-приложения, использующего OpenSplice DDS (6.1.0p5, распределение PrismTech) внутри Tomcat (8.0.21) с Oracle JRE (1.8u40).
Фон
Наш код использует библиотеки OpenSplice dcpscj.jar, dcpssaj.jar, dlrlsaj.jar. По причинам лицензирования и обслуживания они размещаются во внешнем каталоге /opt/OpenSpliceDDS/V6.1.0p6/HDE/x86.linux2.6/jar, а не встраиваются в файл WAR в обычном WEB-INF/lib.
- Я успешно сделал это доступным для нашего веб-приложения, установив переменную CLASSPATH в необязательном файле bin/setenv.sh, в соответствии с разделом Включение внешнего jar- файла в Tomcat ClassPath.
- Я также установил java.library.path, чтобы заставить части JNI работать, согласно Как добавить нативную библиотеку в Tomcat?,
setenv.sh
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
Я также успешно сделал библиотеки доступными через свойство common.loader в conf/catalina.properties, согласно документации по загрузке классов tomcat.
проблема
Как с CLASSPATH, так и с подходом common.loader Tomcat постоянно аварийно завершает работу с SIG_SEGV при развертывании нашей WAR.
#
# 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, который мы упаковываем как отдельный JAR-файл в WEB-INF/lib.
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, который вызывает segfault.
javaClass = (*(ctx->javaEnv))->FindClass (ctx->javaEnv, classDescriptor);
Согласно документации FindClass, используемый загрузчик классов - это тот, который содержит собственный метод.
FindClass находит загрузчик класса, связанный с текущим нативным методом; то есть загрузчик класса, который объявил нативный метод. Если нативный метод принадлежит системному классу, загрузчик классов не будет задействован
Это означает, что загрузчик классов используется для загрузки FooTypeSupportImpl, который находится в dcpssaj.jar. Этот загрузчик классов не может видеть наши определения тем, которые находятся в WEB-INF/lib/themes.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
Вопрос
- Есть ли способ включить дополнительные файлы JAR в Tomcat и загрузить их тем же загрузчиком классов, который использовался для загрузки других файлов JAR в WEB-INF/lib? Я ищу решение на основе чистой конфигурации - я уже рассматривал обходные пути, включая символические ссылки или трансплантацию файлов JAR DDS в файл WAR с помощью некоторого сценария во время развертывания.
- Есть ли способ настроить OpenSplice DDS, чтобы избежать этой проблемы?