I have a custom JNI library that I've compiled on my Ubuntu 20.04 machine - lets call it JNI.so. I also have 2 different Spring Boot webapps that I'm deploying onto my Tomcat 9 server - both of which need access to this library file. Following the instructions here I have tried both adding a JniLifecycleListener to my server.xml configuration file as well as programatically calling org.apache.tomcat.jni.Library.loadLibrary. Although my goal is to get two webapps accessing this native library, to simply debugging this problem I've restricted myself to just one webapp until I get up and running. Here's what my java class looks like:
package com.example.hellojni;
public class HelloJni {
public native String stringFromJNI();
public String test() {
return stringFromJNI();
}
}
And here is the C code:
#include <string.h>
#include <jni.h>
jstring
Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
jobject thiz )
{
return (*env)->NewStringUTF(env, "Hello from JNI !");
}
Compiled with the following:
gcc -c -fPIC hello-jni.c -o hello-jni.o -I/usr/lib/jvm/java-8-openjdk-amd64/include/ -I/usr/lib/jvm/java-8-openjdk-amd64/include/linux
gcc hello-jni.o -shared -o libJNI.so
I've also placed my JNI.so file in $CATALINA_HOME/shared/lib and added the following to my catalina.properties file (as specified here):
shared.loader="${catalina.base}/shared/lib", "${catalina.base}/shared/lib/*.jar"
With the JniLifecycleListener line in my server.xml file, I see the following log data when tomcat boots up:
17-Jun-2020 15:32:06.266 INFO [main] org.apache.catalina.startup.Catalina.load Server initialization in [705] milliseconds
17-Jun-2020 15:32:06.267 INFO [main] org.apache.catalina.core.JniLifecycleListener.lifecycleEvent Loaded native library [JNI]
17-Jun-2020 15:32:06.329 INFO [main] org.apache.catalina.core.StandardService.startInternal Starting service [Catalina]
17-Jun-2020 15:32:06.331 INFO [main] org.apache.catalina.core.StandardEngine.startInternal Starting Servlet engine: [Apache T
However when I try and access the library from within a Spring Boot controller with the following code:
System.out.println(new HelloJni().test());
I get the following error:
java.lang.UnsatisfiedLinkError: com.example.hellojni.HelloJni.stringFromJNI()Ljava/lang/String;
at com.example.hellojni.HelloJni.stringFromJNI(Native Method) ~[classes/:0.0.1-SNAPSHOT]
at com.example.hellojni.HelloJni.test(HelloJni.java:9) ~[classes/:0.0.1-SNAPSHOT]
at com.company.MyApplication.controllers.DashboardController.dashboard(DashboardController.java:51) ~[classes/:0.0.1-SNAPSHOT]
Now, I feel like I've tried every solution I have been able to find online all with pretty much the same results. I tried scrapping using of the use of the JniLifecycleListener altogether and instead reverted back to this popular solution that appears to predate Tomcat 7 but with the exact same results. I've tried locating the .so file in $CATALINA_HOME/lib instead of shared/lib with similar results.
The only way I've been able to make any headway is by scrapping the JniLifecycleListener, calling plain System.loadLibrary() in a static block of code inside a JAR that I've packed up and dropped into shared/lib along with the .so file and then call into the native method call. However, this only works while IN a static method or that static section of code that loads the library. I guess at the very least it confirmed that my JNI libraries are written correctly.
Can anyone help me, I feel like I'm going crazy!