PDA

View Full Version : [SOLVED] JNI with C++ : UnsatisfiedLinkError



zobayer1
June 19th, 2013, 08:24 AM
It was working when I wrote the Hello World version of it, i.e. with a void function, but when I add return types, it continuously gives UnsatisfiedLinkError exception. Here is what I am trying:

The java file [HelloJNI.java]


import java.io.*;
import java.util.*;

public class HelloJNI {
static {
try {
System.loadLibrary("hello");
} catch(UnsatisfiedLinkError e) {
System.out.println(e);
System.exit(1);
}
}

public static native boolean sayHello(String name);

public static void main(String[] args) {
BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
String name;
boolean ret;
try {
while((name = stdin.readLine()) != null) {
if(name.equals("EXIT")) {
break;
}
ret = sayHello(name);
System.out.println(ret);
}
} catch(IOException e) {
System.out.println(e);
}
}
}


javah generated the following file: [HelloJNI.h]


/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloJNI */

#ifndef _Included_HelloJNI
#define _Included_HelloJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: HelloJNI
* Method: sayHello
* Signature: (Ljava/lang/String;)Z
*/
JNIEXPORT jboolean JNICALL Java_HelloJNI_sayHello
(JNIEnv *, jclass, jstring);

#ifdef __cplusplus
}
#endif
#endif


Here is the actual logic of the C++ program:
[HelloJNILogic.h]


#ifndef _HELLO_JNI_LOGIC_H
#define _HELLO_JNI_LOGIC_H

#ifdef __cplusplus
extern "C" {
#endif

bool sayHello(const char* name);


#ifdef __cplusplus
}
#endif

#endif


[HelloJNILogic.cpp]


#include "HelloJNILogic.h"

#include <cstring>

bool sayHello(const char* name) {
int len = strlen(name);
return len % 2 == 0;
}


And finally here is the C file for interfacing C++ and JNI together: [HelloJNIInterface.c]


#include <jni.h>

#include "HelloJNI.h"
#include "HelloJNILogic.h"

JNIEXPORT jboolean JNICALL Java_HelloJNI_sayHello(JNIEnv *env, jobject obj, jstring name) {
const char *str = env->GetStringUTFChars(name, 0);
bool ret = sayHello(str);
env->ReleaseStringUTFChars(name, str);
if(ret) return JNI_TRUE;
return JNI_FALSE;
}


Finally this is the commands I am using to compile and execute the program, I have put them in a sh file for the ease of work [test.sh]


javac HelloJNI.java
javah HelloJNI
g++ -I"$JAVA_HOME"/include -I"$JAVA_HOME"/include/linux -shared -fpic -o libhello.so HelloJNIInterface.c HelloJNILogic.cpp
java -Djava.library.path=. HelloJNI


When I execute program, it starts and waits for my input and when input is supplied, it shows the UnsatisfiedLinkError exception. How to find the bug? Thanks in advance :)

zobayer1
June 19th, 2013, 09:26 AM
After trying a bit I figured out the problem.
Here the signature is something like:

JNIEXPORT jboolean JNICALL Java_HelloJNI_sayHello (JNIEnv *, jclass, jstring);


However, the tutorial I was following had jobject instead of parameter jclass, so after changing this in the above code [HelloJNIInterface.c], the problem is solved.

Is it possible to do this with .a files instead of .so files?

spjackson
June 19th, 2013, 10:23 AM
Is it possible to do this with .a files instead of .so files?
Not currently. See http://openjdk.java.net/jeps/178

zobayer1
June 19th, 2013, 10:28 AM
Not currently. See http://openjdk.java.net/jeps/178

Thanks man, just one more question, if I just change the .so file, should the entire jni class needs to be recompiled? or if the class is recompiled, do I need to compile the .so file again?

dwhitney67
June 19th, 2013, 11:16 AM
Thanks man, just one more question, if I just change the .so file, should the entire jni class needs to be recompiled? or if the class is recompiled, do I need to compile the .so file again?

Describe what you mean by "changing" the library? Whereas you may change the implementation within functions, you may not change the signature of existing functions that are called upon by your JNI layer. If you perform the latter, then obviously you will need to remake your entire project.

Btw, the JNI layer does not need to be in a C file; you should consider placing it within a C++ file. For more complex projects, you may it useful to employ the use of C++ constructs in the JNI layer.

zobayer1
June 19th, 2013, 11:52 AM
Describe what you mean by "changing" the library? Whereas you may change the implementation within functions, you may not change the signature of existing functions that are called upon by your JNI layer. If you perform the latter, then obviously you will need to remake your entire project.

Btw, the JNI layer does not need to be in a C file; you should consider placing it within a C++ file. For more complex projects, you may it useful to employ the use of C++ constructs in the JNI layer.

Yeah later I have changed that to a cpp file. And (hopefully) only the algorithm in body will change, but the signature of the function will not change. So, I only need to re-compile the part which is changed and not the whole project if I modify the body of the function but not the signature.

dwhitney67
June 19th, 2013, 12:07 PM
So, I only need to re-compile the part which is changed and not the whole project if I modify the body of the function but not the signature.
That's correct. Try an experiment... insert a call to std::cout in your C++ library; then rebuild the library.

Then, merely execute your Java program (there's no need to recompile it). You should see the statement outputted by the cout.

Leuchten
June 19th, 2013, 07:14 PM
OP, if you're feeling particularly daring, you might try bridj. It's a way to call native libraries like JNA, but it is compatible with C++. The difference between it and JNI is that you write java code for the bindings instead of the C/C++ bindings you write with JNI.

zobayer1
June 20th, 2013, 06:09 AM
OP, if you're feeling particularly daring, you might try bridj. It's a way to call native libraries like JNA, but it is compatible with C++. The difference between it and JNI is that you write java code for the bindings instead of the C/C++ bindings you write with JNI.

Well, surely I will give that a try, but in this project, I have to use JNI as other modules are build up using the similar fashion. Thanks for letting me know about that :)