Consider the simplest possible example of using JNI consisting of a C file
#include <jni.h>
#include <stdio.h>
#include <syslog.h>
JNIEXPORT void JNICALL Java_org_example_hi_sayHi(JNIEnv*, jobject) {
puts("hi from native code");
syslog(LOG_NOTICE, "hi from native code");
}
and Java file hi.java
in the org/example
subdirectory:
package org.example;
public class hi {
// THE LINE REFERENCED BELOW: static { System.loadLibrary("hi"); }
public native void sayHi();
}
Compiling and running the following Java program
import org.example.hi;
public class sayhi {
public static void main(String[] args) {
System.loadLibrary("hi");
new hi().sayHi();
}
}
works perfectly fine.
However doing the same thing from jshell prompt fails:
$ jshell -v
| Welcome to JShell -- Version 11.0.8
| For an introduction type: /help intro
jshell> import org.example.hi;
jshell> System.loadLibrary("hi");
jshell> new hi().sayHi();
| Exception java.lang.UnsatisfiedLinkError: 'void org.example.hi.sayHi()'
| at hi.sayHi (Native Method)
| at (#3:1)
and I don't understand why. Loading the library succeeds, as can be seen by either using a non-existing library name in jshell (this results in an error) or by examining the child java
process used by jshell
process using lsof
: I can see that libhi.so
is not used by it before the loadLibrary()
call but is used after it.
Moreover, uncommenting the commented out line in hi.java
makes it work in jshell too, i.e. if loadLibrary()
is executed as part of loading the package and not typed at jshell prompt itself, everything works as expected (except that puts()
output is nowhere to be seen, apparently because java
stdout is redirected somewhere, but syslog()
output can be seen and there is no UnsatisfiedLinkError
).
But it seems like doing this in jshell should work too -- yet it doesn't. Does anybody have an explanation for this?
FWIW this is not Linux-specific, it is just simpler to test this under Linux. I observe exactly the same behaviour with Java 12 under Windows too.