1

I am trying to call a simple native method in Java from C++, to achieve this I do the following:

  1. Create a simple class as shown below:

    public class HelloNative{
       public native void printString(String str);
       static{
            System.out.println("Current Directory is: " + System.getProperty("user.dir"));
            System.load(System.getProperty("user.dir") + "/libhellonative.so");
        }
        public static void main(String[] args){
            System.out.println("Calling Native Libraray (libhellonative.so) method printString");
            new HelloNative().printString("Message from Java to C");
         }
    }
    
  2. Create .h file for the native method using javah -jni which create the following declaration:

    JNIEXPORT void JNICALL Java_HelloNative_printString(JNIEnv *, jobject, jstring);

  3. Implement the native function in .cpp file as:

    JNIEXPORT void JNICALL Java_HelloNative_printString(JNIEnv* jni_env, jobject java_obj, jstring msg){
        printf("inside native method\n");
        jboolean iscopy;
        const char *message = (jni_env)->GetStringUTFChars( msg, &iscopy);
        printf("%s", message);
    }
    
  4. And finally create the .so file using:

    g++ HelloNative.cpp -o libhellonative.so -shared -Wl,-soname,libhellonative.so -static -lc -I /usr/lib/jvm/java-6-sun-1.6.0.26/include -I /usr/lib/jvm/java-6-sun-1.6.0.26/include/linux

But when I compile and run the .java file it's giving me Runtime Exception:

Current Directory is: /home/gmuhammad/Projects/test
Calling Native Libraray (libhellonative.so) method printString
Exception in thread "main" java.lang.UnsatisfiedLinkError: HelloNative.printString(Ljava/lang/String;)V
    at HelloNative.printString(Native Method)
    at HelloNative.main(HelloNative.java:16)
Pratik
  • 30,639
  • 18
  • 84
  • 159
gmuhammad
  • 1,434
  • 4
  • 25
  • 39

3 Answers3

3

Ok, I got this to work. It has nothing to do with loading the library, but with actually calling the method from that library.

I created .java, .h and .cpp files by copy/paste from your question and ran them (I had to add #include <jni.h> in the .cpp file) - and got exactly the same error as you did.

Then I edited the .cpp file to include the generated .h file. Also, as maba indicated in his answer, you need to call (jni_env)->ReleaseStringUTFChars(msg, message); to release the object. My full .cpp file now looks like this:

#include "HelloNative.h"

JNIEXPORT void JNICALL Java_HelloNative_printString(JNIEnv* jni_env, jobject java_obj, jstring msg){
    printf("inside native method\n");
    jboolean iscopy;
    const char *message = (jni_env)->GetStringUTFChars( msg, &iscopy);
    printf("%s", message);
    (jni_env)->ReleaseStringUTFChars(msg, message);
}

I re-compiled the library, ran the java code - and voila! everything works. This way, it works regardless of which way you load the library, with load or loadLibrary.

Give it a try.

Aleks G
  • 56,435
  • 29
  • 168
  • 265
  • got it working now. :D. Thanks, but one more thing, why do I need to call (jni_env)->ReleaseStringUTFChars(msg, message); as the string which is got printed is passing from the java environment. Should not it be garbage collected by jvm? – gmuhammad Aug 21 '12 at 16:25
  • Still waiting for an answer and confused about the above question.... will be more thankful if someone can explain... – gmuhammad Aug 21 '12 at 16:36
  • @gmuhammad Have a look at this question: http://stackoverflow.com/questions/5859673/should-you-call-releasestringutfchars-if-getstringutfchars-returned-a-copy - the question itself answers your question. – Aleks G Aug 22 '12 at 10:52
1

I recommend to load the library slightly differently.

  1. Place your libmynative.so into whatever directory you like.

  2. Add that directory to LD_LIBRARY_PATH variable.

  3. In your java code, change System.load call to be System.loadLibrary("mynative") (note the lack of full path, prefix lib and extension .so)

  4. Run your java code.

Have a look here for more details: http://java.sun.com/developer/onlineTraining/Programming/JDCBook/jniexamp.html

Aleks G
  • 56,435
  • 29
  • 168
  • 265
  • tried what you suggest but I got following error: Current Directory is: /home/gmuhammad/Projects/test Exception in thread "main" java.lang.UnsatisfiedLinkError: no libhellonative.so in java.library.path at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1738) at java.lang.Runtime.loadLibrary0(Runtime.java:823) at java.lang.System.loadLibrary(System.java:1028) at HelloNative.(HelloNative.java:11) Could not find the main class: HelloNative. Program will exit. – gmuhammad Aug 21 '12 at 15:02
  • just adding the value of the environment variable you suggested: $ echo $LD_LIBRARY_PATH /home/gmuhammad/Projects/test – gmuhammad Aug 21 '12 at 15:08
  • @gmuhammad (1) Before loading the library, do `System.out.println(System.getProperty("java.library.path"));` and check that the directory where the library is located is included there. (2) Did you copy/paste my code? I misnamed the library file - make sure it's correct. – Aleks G Aug 21 '12 at 15:27
  • /usr/lib/jvm/java-6-sun-1.6.0.26/jre/lib/i386/server:/usr/lib/jvm/java-6-sun-1.6.0.26/jre/lib/i386:/usr/lib/jvm/java-6-sun-1.6.0.26/jre/../lib/i386:/home/gmuhammad/Projects/test:/usr/java/packages/lib/i386:/lib:/usr/lib That's what I got – gmuhammad Aug 21 '12 at 15:32
  • Why it is giving different value of java library path, when I printed using echo $LD_LIBRARY_PATH, and totally different value when printed using System.out. Is there anything I am missing? – gmuhammad Aug 21 '12 at 15:39
  • @gmuhhamad LD_LIBRARY_PATH is your environment variable. Internally, JVM will look for native libraries in all directories in the java.library.path variable, which among other things includes LD_LIBRARY_PATH directories – Aleks G Aug 21 '12 at 15:44
  • which means it should include /home/gmuhammad/Projects/test (which is LD_LIBRARY_PATH in my case), but as a matter of fact it is not printing in the System.out statement. am I right? – gmuhammad Aug 21 '12 at 15:52
  • should I copy my .so file in one of these directory which System.out is printing? Is it necessary? Why it is not loading from current directory. I do not understand what I am missing at the moment. – gmuhammad Aug 21 '12 at 15:54
1

It is totally OK to use the System.load() but you will have to be sure that your shared libraries really are where you say they should be.

Current Directory is: /home/gmuhammad/Projects/test

Your code is trying to access the shared library in that directory. Are you sure that the libhellonative.so is there?

Then you can also use the System.mapLibraryName("hellonative") to get the name of the shared library for the current platform. That makes it more platform independent. The call will give you libhellonative.so on linux and hellonative.dll on windows.

Also you must call (jni_env)->GetReleaseUTFChars(msg, message); to release the objects.

maba
  • 47,113
  • 10
  • 108
  • 118
  • libraray is in my current directory, that is why I use the System.getProperty("user.dir"). – gmuhammad Aug 21 '12 at 15:02
  • adding the directory listing: drwxr-xr-x 2 gmuhammad gmuhammad 4096 2012-08-21 19:58 . drwxr-xr-x 8 gmuhammad gmuhammad 4096 2012-08-21 16:43 .. -rw-r--r-- 1 gmuhammad gmuhammad 950 2012-08-21 19:58 HelloNative.class -rw-r--r-- 1 gmuhammad gmuhammad 351 2012-08-21 18:06 HelloNative.cpp -rw-r--r-- 1 gmuhammad gmuhammad 421 2012-08-21 16:51 HelloNative.h -rw-r--r-- 1 gmuhammad gmuhammad 485 2012-08-21 19:58 HelloNative.java -rwxr-xr-x 1 gmuhammad gmuhammad 7056 2012-08-21 18:06 libhellonative.so – gmuhammad Aug 21 '12 at 15:23