I am using Python and Py4J to test JNI code. But when I call the JNI code I get the following error:
py4j.protocol.Py4JJavaError: An error occurred while calling o37.createInstance.
: java.lang.UnsatisfiedLinkError: com.mgr_api_JNI.createInstance(Lcom/mgr_api_types$EDisplayType;Ljava/lang/String;Lcom/mgr_api_types$ECommType;Ljava/lang/String;)V
at com.mgr_api_JNI.createInstance(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at py4j.reflection.MethodInvoker.invoke(MethodInvoker.java:244)
at py4j.reflection.ReflectionEngine.invoke(ReflectionEngine.java:357)
at py4j.Gateway.invoke(Gateway.java:282)
at py4j.commands.AbstractCommand.invokeMethod(AbstractCommand.java:132)
at py4j.commands.CallCommand.execute(CallCommand.java:79)
at py4j.GatewayConnection.run(GatewayConnection.java:238)
at java.base/java.lang.Thread.run(Thread.java:834)
I have looked at these links link 1, link 2, link 3, link 4, link 5, and link 6, plus others, but none of them solve my problem.
Code
mgr_api_JNI.java:
package com;
import com.mgr_api_types.*;
public class mgr_api_JNI
{
static
{
try
{
System.loadLibrary("Mngr"); // Use "-Djava.library.path=[path to library]" option to load this library
}
catch (UnsatisfiedLinkError e)
{
System.err.println("Native code library 'Mngr' failed to load.\n" + e);
}
}
public native void createInstance(com.mgr_api_types.EDisplayType displayType,
String displaySerialNumber,
com.mgr_api_types.ECommType commType,
String portName);
}
testsJNI.java:
import com.*;
import py4j.GatewayServer;
public class testsJNI
{
public static void main(String args[])
{
testsJNI testApp = new testsJNI();
// Py4J server
GatewayServer server = new GatewayServer(testApp);
server.turnLoggingOff();
server.start();
}
}
com_mgr_api_JNI.h (created by using javac -h on mgr_api_JNI.java):
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_mgr_api_JNI */
#ifndef _Included_com_mgr_api_JNI
#define _Included_com_mgr_api_JNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_mgr_api_JNI
* Method: createInstance
* Signature: (Lcom/mgr_api_types/EDisplayType;Ljava/lang/String;Lcom/mgr_api_types/ECommType;Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_com_mgr_1api_1JNI_createInstance
(JNIEnv *, jobject, jobject, jstring, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
com_mgr_api_JNI.cpp:
#include "com_mgr_api_JNI.h"
static manager::CManagerApi* manager = NULL;
JNIEXPORT void JNICALL Java_com_mgr_1api_1JNI_createInstance(
JNIEnv *env,
jobject thisObj,
jobject displayType,
jstring displaySerialNumber,
jobject commType,
jstring portName)
{
manager::EDisplayType dType = convertObjectToDisplayType(env, displayType);
const char* serialNumber = env->GetStringUTFChars(displaySerialNumber, 0);
manager::ECommType comm = convertObjectToCommType(env, commType);
const char* port = env->GetStringUTFChars(portName, 0);
char buf[100];
sprintf(buf,"%s",port);
std::string portStr = buf;
sprintf(buf,"%s",serialNumber);
std::string serialNumStr = buf;
if (manager == NULL)
{
manager = manager::CManagerApi::get();
manager->initialize(dType, serialNumStr, comm, portStr);
}
// Release memory
env->ReleaseStringUTFChars(displaySerialNumber, serialNumber);
env->ReleaseStringUTFChars(portName, port);
}
Command line execution of Java code:
java -cp /mnt/c/Workspace/library/java/:.:/home/fred/.local/share/py4j/py4j0.10.7.jar -Djava.library.path=/mnt/c/Workspace/build/library/ testsJNI
Doing a -XshowSettings:properties
shows the following properties:
awt.toolkit = sun.awt.X11.XToolkit
file.encoding = UTF-8
file.separator = /
java.awt.graphicsenv = sun.awt.X11GraphicsEnvironment
java.awt.printerjob = sun.print.PSPrinterJob
java.class.path = /mnt/c/Workspace/library/java/
.
/home/fred/.local/share/py4j/py4j0.10.7.jar
java.class.version = 55.0
java.home = /usr/lib/jvm/java-11-openjdk-amd64
java.io.tmpdir = /tmp
java.library.path = /mnt/c/Workspace/build/library/
Attempts to solve the problem
Making sure I have a valid native library
Doing an ls
on the java.library.path /mnt/c/Workspace/build/library/
shows the library libMngr.so
.
If I delete libMngr.so
from that location and then try to run Java complains it can't find the library.
Doing the nm
command on libMngr.so
shows the following:
000000000021f269 T Java_com_mgr_1api_1JNI_createInstance
So Java can find the native library and it has the symbol of the function.
Doing the objdump
command:
$objdump -f build/library/libMngr.so
build/library/libMngr.so: file format elf64-x86-64
architecture: i386:x86-64, flags 0x00000150:
HAS_SYMS, DYNAMIC, D_PAGED
start address 0x000000000018aee0
Shows that the native library is 64 bit and according to the -XshowSettings:properties
I am using 64 bit Java.
I have even put a print statement right before System.loadLibrary("Mngr");
in mgr_api_JNI.java
to make sure that the native library is loaded only once.
Update
I have regenerated the header file from mgr_api_JNI.java
and copied the function declaration to the .cpp
file to make sure the function name is correct. But I still get the same error.
Making sure I have valid Java classes
If I do an ls
on the java.class.path /mnt/c/Workspace/library/java/
I find all of the Java classes from compiling mgr_api_JNI.java
.
If I delete the classes and try to run Java, then Java complains it can't find the classes.
Doing a grep -r createInstance
on the java.class.path /mnt/c/Workspace/library/java/
returns:
Binary file com/mgr_api_JNI.class matches
com/mgr_api_JNI.java: public native void createInstance(com.mgr_api_types.EDisplayType displayType,
So Java can find the the compiled Java class and it has the createInstance
method in it.
Questions
It appears to me that Java can find all of the needed classes and the native library, but I am still getting the UnsatisfiedLinkError
error.
Why am I still getting this error?
What do I need to do to help Java find/recognize the createInstance
method?