简体   繁体   中英

How do I avoid UnsatisfiedLinkError when calling C++ from java from C++ application?

I'm embedding Java into a C++ application. As part of this I need to expose native functions to java, as well as calling java functions from C++.

Do I need to put the functions I want to call from java into a shared library? Or can they be compiled into the host application somehow?

Here's what I've tried so far, but it gives a java.lang.UnsatisfiedLinkError

Compilation

I'm building on OS X 10.5 using

g++ -Wall -I/System/Library/Frameworks/JavaVM.framework/Headers/ -framework JavaVM -g test.cpp

Java Test File : TestObject.java

// To build this you need to do a `javac TestObject.java`
// To get the signatures do a `javap -d TestObject`
// To generate the .h file do a `javah TestObject`
public class TestObject
{
    public native TestObject get_property( String k ); 
}

C++ Test File : test.cpp

#include <jni.h>
#include <assert.h>


JNIEXPORT jobject JNICALL Java_TestObject_get_1property(JNIEnv * jni_env, jobject obj, jstring key)
{
  //Just a stub implementation for now.
  jclass klass = jni_env->GetObjectClass( obj );
  jmethodID constructor = jni_env->GetMethodID( klass, "<init>", "()V");
  jobject retval = jni_env->NewObject(klass, constructor );

  return retval;
}




int main()
{
  JavaVM* jvm;
  JavaVMInitArgs vm_args;
  JavaVMOption options[1];

  vm_args.version = JNI_VERSION_1_4;
  vm_args.nOptions = 1;
  options[0].optionString = "-Djava.class.path=.";
  vm_args.options = options;
  vm_args.ignoreUnrecognized = JNI_FALSE;

  JNIEnv * env;
  JNI_CreateJavaVM(&jvm, (void **)&env, &vm_args);

  jclass klass = (env)->FindClass("TestObject");
  assert( klass );

  jmethodID constructor = env->GetMethodID( klass, "<init>", "()V");
  assert( constructor );

  jobject obj = env->NewObject(klass, constructor );

  jmethodID test_method = (env)->GetMethodID( klass, "get_property", "(Ljava/lang/String;)LTestObject;" );
  assert( test_method );

  jvalue args[1];
  args[0].l = env->NewStringUTF("k");

  jobject rv = env->CallObjectMethodA(obj, test_method, args );

  jthrowable exc = env->ExceptionOccurred();
  if(exc)
  {
    env->ExceptionDescribe();
    env->ExceptionClear();
  }

  //TODO: do something with rv

}

Normally the JVM expects to find native method definitions in a shared library that has been loaded via System#load or System#loadLibrary , and in most cases that is the most convenient approach. However, there does exist an alternative for situations like yours, where you would prefer to include the implementations directly in your executable.

If you call JNIEnv::RegisterNatives , you can instead pass the JVM a list of function pointers corresponding to the native methods in a particular class. When some Java code calls one of those methods, the JVM will know to invoke the function pointer you passed to RegisterNatives instead of searching through dynamically-loaded libraries.

JNINativeMethod methods[] = {
    {
        "frobFabulously",
        "(Ljava/lang/Object;)V",
        reinterpret_cast<void*>(NativeFrobFabulouslyImpl)
    },
};

env->RegisterNatives(clazz, methods, sizeof(methods)/sizeof(JNINativeMethod));

It's been a while since I've messed with JNI, so I'm a little rusty on the topic. I think your problem is that you're declaring the get_property method as native . This means that the JVM expects to find a shared library exposing the get_property method. Here's the documentation on java.lang.UnsatisfiedLinkError .

UnsatisfiedLinkError is thrown when (1) attempting to call a native method that has not been loaded or (2) when loadLibrary or load method in Runtime or System is called for a file that cannot be found.

You declare a Java method native only if you're going to implement that method in C or C++ and then call it from Java. Since you're trying to do the opposite, ie call Java methods from native code, you need to actually implement the get_property method in Java. In native code you'll then create a class instance of TestObject and call the get_property method on this instance.

I found a Sun tutorial on how to embed the JVM in native code. The book itself begins with examples of how to call native code from Java.

Try this one: When you execute the Java application, add the missing link file with "LD_LIBRARY_PATH"

Something like

LD_LIBRARY_PATH=[the link file path need be included] java xxx.class

The path can use absolute path. Hope this might be helpful.

I think you should try writing the JNI function in another file. When you javah TestObject.java, a file TestObject.h will be generated. Create a file TestObject.c with the implemented function. Then build a shared library using the native code. ( Something like g++ -G -I/pkgs/jdk1.4/include TestObject.C -o libTestObject.so) Also in TestObject.java, load the library statically like static{ System.loadLibrary("TestIbject"); The libTestObject.so should be added to LD_LIBRARY_PATH ( On a Linux environment)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM