I'm having difficulties running a web application that uses OpenSplice DDS (6.1.0p5, PrismTech distriburation) inside Tomcat (8.0.21) with Oracle JRE (1.8u40).
Background
Our code uses the OpenSplice libraries dcpscj.jar, dcpssaj.jar, dlrlsaj.jar. For licensing and maintenance reasons these are hosted in an external directory /opt/OpenSpliceDDS/V6.1.0p6/HDE/x86.linux2.6/jar rather than being embedded in the WAR file in the usual WEB-INF/lib.
- I've successfully these available to our web application by setting the CLASSPATH variable inside the optional bin/setenv.sh file, as per Including external jar in Tomcat ClassPath.
- I've also set the java.library.path to make the JNI parts work, as per How to add native library in 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
I've also successfully made the libraries available via the common.loader property in conf/catalina.properties, as per tomcat classloading documentation.
Problem
With both CLASSPATH and common.loader approaches Tomcat consistently crashes with a SIG_SEGV when our WAR is deployed.
#
# 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
#
The top part of the stack
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
Analysis
This problem only occurs when JARs are external to WEB-INF/lib, if they are 'embedded' in WEB-INF/lib, Tomcat does not crash.
org.example.dds.example_topic_typeTypeSupport (anonymised) is code generated by OpenSplice that we package as a separate JAR in WEB-INF/lib.
example_topic_typeTypeSupport
calls FooTypeSupportImpl.registerType() which then passes the classname as a String in IDL form "org::example::dds:example_topic_type" into the JNI part saj_fooTypeSupport.c.
It's hard to follow, but I believe eventually env->FindClass is called with the Java variant i.e. org.example.dds.example_topic_type. This appears to be returning NULL which is then passed into jni_GetMethodID which causes the segfault.
javaClass = (*(ctx->javaEnv))->FindClass (ctx->javaEnv, classDescriptor);
According to the FindClass documentation the classloader that is used is the one that hosts the native method.
FindClass locates the class loader associated with the current native method; that is, the class loader of the class that declared the native method. If the native method belongs to a system class, no class loader will be involved
This means that the classloader is the one used to load FooTypeSupportImpl which lives in dcpssaj.jar. This class loader cannot see our topic definitions which live in WEB-INF/lib/topics.jar.
The Tomcat classloading documentation describes private classloaders for each module
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
Question
- Is there anyway to include extra JAR files in Tomcat and have them loaded by the same classloader used to load other JARs in WEB-INF/lib? I'm looking for clean configuration based solution - I've already considered workarounds including symlinks or transplanting the DDS JARs into the WAR file via some script at time of deployment.
- Is there anyway to configure OpenSplice DDS to avoid this issue?