I am trying native methods for the first time....
I had taken a simple programming from this link Click....
nativetest.java
public class nativetest
{
static {
System.loadLibrary("nativetest");
}
public native String sayHello(String s);
public static void main(String[] argv)
{
String retval = null;
nativetest nt = new nativetest();
retval = nt.sayHello("Beavis");
System.out.println("Invocation returned " + retval);
}
}
javac nativetest.java
javah -jni nativetest
Successfully nativetest.h file has been created
nativetest.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class nativetest */
#ifndef _Included_nativetest
#define _Included_nativetest
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: nativetest
* Method: sayHello
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_nativetest_sayHello(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
nativetest.c code
nativetest.c
include "nativetest.h" /*double quotes tells it to search current directory*/
JNIEXPORT jstring JNICALL Java_nativetest_sayHello (JNIEnv *env, jobject thisobject, jstring js)
{
return js;
}
gcc -I/usr/java/jdk1.7.0_13/include -I/usr/java/jdk1.7.0_13/include/linux -o nativetest.so -shared nativetest.c
Successfully shared object file has been created.
when i executed the nativetest, it is showing the following error
java -Djava.library.path=. nativetest
Exception in thread "main" java.lang.UnsatisfiedLinkError: no nativetest in java.library.path
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1860)
at java.lang.Runtime.loadLibrary0(Runtime.java:845)
at java.lang.System.loadLibrary(System.java:1084)
at nativetest.(nativetest.java:4)
Thanx in advance....
On Linux, a shared library's file name should start with "lib", that is, "lib[library_name].so".
Reference: 3.1.1. Shared Library Names
Every shared library has a special name called the ``soname''. The soname has the prefix ``lib'', the name of the library, the phrase ``.so'', followed by a period and a version number that is incremented whenever the interface changes (as a special exception, the lowest-level C libraries don't start with ``lib''). A fully-qualified soname includes as a prefix the directory it's in; on a working system a fully-qualified soname is simply a symbolic link to the shared library's ``real name''.
Related
This is my first attempt at JNI. My ultimate goal is to get all tasks currently running on a machine, but need to get even a simple example running. I keep getting this error when I try to execute my main program. I have supplied my simple Java main program, the header file generated, and the error.
I do not know what this DLL could be dependent on. It was initially referencing a DLL I tracked down and placed into system32 (msvcr90.dll).
Here is the command I used to compile the C code as well which produced the DLL, OBJ, LIB, EXP and manifest files.
cl -I"C:\Program Files\Java\jdk1.6.0\include" -I"C:\Program Files\Java\jdk1.6.0\include\win32" -MD -LD HelloWorld.c -FeHelloWorld.dll
class HelloWorld {
private native void print();
public static void main(String[] args) {
new HelloWorld().print();
}
static {
System.load("C:\\temp\\HelloWorld.dll");
}
}
#include <jni.h>
#include <stdio.h>
#include "HelloWorld.h"
JNIEXPORT void JNICALL
Java_HelloWorld_print(JNIEnv *env, jobject obj)
{
printf("Hello World!\n");
return;
}
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorld */
#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: HelloWorld
* Method: print
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_HelloWorld_print
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
java.lang.UnsatisfiedLinkError: C:\temp\HelloWorld.dll: A dynamic link library (DLL) initialization routine failed
at java.lang.ClassLoader$NativeLibrary.load(Native Method)
at java.lang.ClassLoader.loadLibrary0(Unknown Source)
at java.lang.ClassLoader.loadLibrary(Unknown Source)
at java.lang.Runtime.load0(Unknown Source)
at java.lang.System.load(Unknown Source)
at HelloWorld.<clinit>(HelloWorld.java:7)
Exception in thread "main"
The Unsatisfied Link Error can mean many things went wrong. I would use
System.loadLibrary("HelloWorld");
Instead of
System.load();
As TwentyMiles suggested.
Also, when invoking your program you need to (assuming your DLL is on the same directory as your class files:
java -Djava.library.path=. HelloWorld
Here's a simple demo I made that calls a Win32 API function (MessageBox)
Java class
class CallApi{
private native String showMessageBox(String msg);
private native double getRandomDouble();
static{
try{
System.loadLibrary("CallApi");
System.out.println("Loaded CallApi");
}catch(UnsatisfiedLinkError e){
//nothing to do
System.out.println("Couldn't load CallApi");
System.out.println(e.getMessage());
}
}
public static void main(String args[]){
CallApi api = new CallApi();
double randomNumber = api.getRandomDouble();
String retval = api.showMessageBox("Hello from Java!\n"+
"The native random number: "+randomNumber);
System.out.println("The native string: "+retval);
}
}
Generated header file
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class CallApi */
#ifndef _Included_CallApi
#define _Included_CallApi
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: CallApi
* Method: showMessageBox
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_CallApi_showMessageBox
(JNIEnv *, jobject, jstring);
/*
* Class: CallApi
* Method: getRandomDouble
* Signature: ()D
*/
JNIEXPORT jdouble JNICALL Java_CallApi_getRandomDouble
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
The C DLL code
#include "CallApi.h"
#include <windows.h>
#include <stdlib.h>
#include <time.h>
#pragma comment(lib,"user32.lib")
JNIEXPORT jstring JNICALL Java_CallApi_showMessageBox
(JNIEnv *env, jobject thisObject, jstring js)
{
//first convert jstring to const char for use in MessageBox
const jbyte* argvv = (*env)->GetStringUTFChars(env, js, NULL);
char* argv =(char *) argvv;
//Call MessageBoxA
MessageBox(NULL, argv, "Called from Java!", MB_ICONEXCLAMATION | MB_OK);
return js;
}
JNIEXPORT jdouble JNICALL Java_CallApi_getRandomDouble
(JNIEnv *env, jobject thisObject)
{
double num1;
srand((unsigned)(time(0)));
num1 = ((double)rand()/(double)RAND_MAX);
return num1;
}
Compile instructions
I compile with the Visual C++ express 2008 cl, removing the -ML flag since it causes an exception when the Java code tries to call the native code:
cl /I"c:\Program Files\Java\jdk1.6.0_10\include" /I"c:\Program Files\Java\jdk1.6.0_10\include\win32" -LD CallApi.c -FeCallApi.dll
Then, to run the code:
java -Djava.library.path=. CallApi
I'm not claiming to understand the situation enough to explain it, however some users reported the error when using the "-MD" compiler flag.
For more information see Java Native Interface (JNI) - Impossible to use VS2005 with Java? which discusses this problem and offers possible work-arounds and think techie blog for alternatives.
I believe that you should be using
System.loadLibrary("HelloWorld");
instead of System.load. LoadLibrary will check your system path (not the Java library path) so make sure that HelloWorld.dll is in a directory where it can found. Also note that it does not require the full path, and you don't need to add the dll extension to the end.
I just removed -MD option and compiled it worked like charm
cl -I"C:\Program Files\Java\jdk1.6.0_21\include" -I"C:\Program Files\Java\jdk1.6.0_21\include\win32" -LD HelloWorld.c -FeHelloWorld.dll
If you change the location (package) of your native function declaration from the java side without updating the h file, and the signature of the method in the c++ side, It won't resolve to the method and will throw unsatisfied..
package x;
public class A {
private native void print();
...
}
moved to:
package x.y;
public class A {
private native void print();
...
}
This will require regeneration of the H file (to something like Java_x_y_A_print ).
Note you can change those signatures manually but I won't recommend
I am trying to link a Java swing frontend to the c++ backend using JNI. I am able to generate a class file and header file for that class. I could also write the .cpp file and also generate a library file. But when I try to run my application I get an error undefined symbol: __gxx_personality_v0.
I went through many examples on stackoverflow but none of it worked in my case. I made sure I have libgcc, libgcj and libstd++. Also added pragma GCC java_exceptions on the top of the JNI header file.
But none of these worked.
Java file - InitJNI.java
public class InitJNI {
public native String callMethod(String str);
public static void init(String s){
System.out.println("-------inside InitJNI.java--------");
System.loadLibrary("eg1");
System.out.println("Library loaded in InitJNI--------");
InitJNI init1 = new InitJNI();
init1.callMethod(s);
}
}
JNI header file - InitJNI.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class org_openscience_jchempaint_application_InitJNI */
#ifndef _Included_InitJNI
#define _Included_InitJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: InitJNI
* Method: callMethod
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_InitJNI_callMethod
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
Cpp file - InitJNI.cpp
#include "InitJNI.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#pragma GCC java_exceptions
JNIEXPORT jstring JNICALL Java_InitJNI_callMethod
(JNIEnv * env, jobject, jstring string){
char buf[128];
const char *str = env->GetStringUTFChars(string, NULL);
printf("%s", str);
env->ReleaseStringUTFChars(string, str);
}
I am trying to create a shared lib using command:
g++ -shared -fpic -o libeg1.so -I/usr/java/include -I/usr/java/include/linux InitJNI.cpp
Which generates libeg1.so file. But my application does not run. It gives me
undefined symbol: __gxx_personality_v0
Any help with this is appreciated.
Thank you!
I know this is an often-seen problem and after search on the web, problem still not solved. -Djava.library.path is already added to the command.
Here is my java file
public class TestJni
{
public native void print(A a);
static{
System.loadLibrary("MyJni");
}
public static void main(String []args){
new TestJni().print(new A());
}
}
class A{
void show(){
System.out.println("In a show function");
}
}
I am trying to create an instance 'a' which is A class.
Pass it to native method print and let native method to invoke show() method.
Compile .java into .class
javac TestJni.java
Now I have three files: A.class TestJni.class TestJni.java
Use
javah -classpath . TestJni
to get .h file which is like:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class TestJni */
#ifndef _Included_TestJni
#define _Included_TestJni
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: TestJni
* Method: print
* Signature: (LA;)V
*/
JNIEXPORT void JNICALL Java_TestJni_print
(JNIEnv *, jobject, jobject);
#ifdef __cplusplus
}
#endif
#endif
Rename it to MyJni.h by
mv TestJni.h MyJni.h
Create MyJni.c by
vim MyJni.c
the content of MyJni.c is:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include "MyJni.h"
/* Header for class TestJni */
#ifndef _Included_TestJni
#define _Included_TestJni
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: TestJni
* Method: print
* Signature: (LA;)V
*/
JNIEXPORT void JNICALL Java_TestJni_print
(JNIEnv *env, jobject obj, jobject o){
jclass cls = (*env)->GetObjectClass(env, o);
jmethodID mid = (*env)->GetMethodID(env, cls, "show", "()V");
(*env)->CallVoidMethod(env, o, mid);
}
#ifdef __cplusplus
}
#endif
#endif
Build .so file by
gcc -fPIC -shared -o libMyJni.so MyJni.c -I. -I$JAVA_HOME/include -I$JAVA_HOME/include/linux
And now I have 5 files [A.class libMyJni.so MyJni.c MyJni.h TestJni.class TestJni.java] in my directory: /home/grid/code/tmp
Run java program:
java -cp . -Djava.library.path=. TestJni
Now I get the Error which is
Exception in thread "main" java.lang.UnsatisfiedLinkError: TestJni.print(LA;)V
at TestJni.print(Native Method)
at TestJni.main(TestJni.java:10)
I am coding under Ubuntu 14.10 and gcc program is the default one.
I really not able to figure out what is wrong.
Please help me to correct the program.
Thank you very much in advance!!!
Remove the
#ifndef _Included_TestJni
#define _Included_TestJni
...
#endif
from the source file. You've already #defined the include guard in the header, so this effectively comments out the code.
Remove the #include "MyJni.h" or
#ifndef _Included_TestJni
#define _Included_TestJni
//..
#endif
Exception in thread "main" java.lang.UnsatisfiedLinkError: TestJni.print(LA;)V this usually happen when JNI can't find the symbol definition in your library. You can use nm to analyze the JNI function you call to see if it is defined and exported in your library.
$ nm libMyJni.so
00000000000005ac T Java_TestJni_print
0000000000200e40 a _DYNAMIC
0000000000200fe8 a _GLOBAL_OFFSET_TABLE_
w _Jv_RegisterClasses
0000000000200e20 d __CTOR_END__
0000000000200e18 d __CTOR_LIST__
You can try using System.load. iirc System.loadLibrary loads .so/.dll files from the default location. System.load should work with an absolute path to the .so/.dll file.
I'm making a simple JNI test app using Eclipse on Windows. My C++ compiler is MinGW 4.6.2. Java is throwing an UnsatisfiedLinkError when I try to invoke a function in my test DLL (the DLL itself loads without problem). I have verified that my DLL exports a "C" function with the same name as the function generated by the javah utility.
How could trying to invoke the function possibly generate a link error? (Also, is there any way to get more detail as to what symbol isn't being found? A bald statement that there's an UnsatisfiedLinkError is next to useless.)
Here's the Java which defines the native function:
package com.xyz.jsdi_test;
import java.io.File;
public class JSDI
{
public static native void func(
String str,
int i,
Integer ii,
long j /* 64 bits */,
Long jj,
byte[] b
);
public static void dummy()
{
System.out.println("JSDI.dummy()");
}
static
{
File f = new File("..\\jsdi\\bin\\jsdi.dll");
System.out.println("Preparing to load: " + f);
System.load(f.getAbsolutePath());
System.out.println("Successfully loaded: " + f);
}
Here is the corresponding output from javah:
...
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_xyz_jsdi_test_JSDI
* Method: func
* Signature: (Ljava/lang/String;ILjava/lang/Integer;JLjava/lang/Long;[B)V
*/
JNIEXPORT void JNICALL Java_com_xyz_jsdi_1test_JSDI_func
(JNIEnv *, jclass, jstring, jint, jobject, jlong, jobject, jbyteArray);
#ifdef __cplusplus
}
#endif
...And how I implemented the function...:
extern "C"
{
JNIEXPORT void JNICALL Java_com_xyz_jsdi_1test_JSDI_func(
JNIEnv * env,
jclass _class,
jstring str,
jint i,
jobject ii,
jlong j,
jobject jj,
jbyteArray b
)
{
// don't do anything...let's just try to get called successfully...
}
} // extern "C"
Here is how I attempt to invoke it.
...
public static void main(String[] args)
{
JSDI.dummy(); // cause class to load, which should cause System.load() to run.
JSDI.func("hello", 0, 0, 0L, 0L, (byte[])null);
}
Finally, this is the output:
Preparing to load: ..\jsdi\bin\jsdi.dll
Successfully loaded: ..\jsdi\bin\jsdi.dll
JSDI.dummy()
java.lang.UnsatisfiedLinkError: com.xyz.jsdi_test.JSDI.func(Ljava/lang/String;ILjava/lang/Integer;JLjava/lang/Long;[B)V
at com.xyz.jsdi_test.JSDI.func(Native Method)
at com.xyz.jsdi_test.SimpleTest.main(SimpleTest.java:24)
SOLVED IT -- WOOO!
It turns out that MSVC prepends an underscore to the name of __stdcall functions. MinGW does not. The Windows JVM apparently expects the '_' prefix. As soon as I prepended '_' to the function name and rebuilt with MinGW, everything worked just dandy.
eg:
JNIEXPORT void JNICALL Java_com_xyz_jsdi_1test_JSDI_func ==> _Java_com_xyz_jsdi_1test_JSDI_func
EDIT:
The --add-stdcall-underscore feature of the dlltool utility included with MinGW can solve this problem for you transparently. Set it up in your Makefile and you don't need to worry about having different versions of the actual source code for different compilers. See at this link.
Posting a working example, copy the content in the three files in the same directory (modify the path to your JDK) then invoke build.cmd
/* File: HelloWorld.java */
public class HelloWorld {
private static native void writeHelloWorldToStdout();
public static void main(String[] args) {
System.loadLibrary("HelloWorld");
writeHelloWorldToStdout();
}
}
/* File: HelloWorld.c */
#include <stdio.h>
#include "HelloWorld.h"
JNIEXPORT void JNICALL Java_HelloWorld_writeHelloWorldToStdout(JNIEnv *env, jclass c)
{
printf("Hello World!");
}
rem File: build.cmd
%echo off
echo delete generated binaries
del HelloWorld.class
del HelloWorld.dll
del HelloWorld.h
del HelloWorld.def
echo Compile the Java Class
javac HelloWorld.java
echo Generate the Header file
javah -classpath . -o HelloWorld.h HelloWorld
echo Build the DLL
gcc -I"C:\Program Files (x86)\Java\jdk1.7.0_25\include" -I"C:\Program Files (x86)\Java\jdk1.7.0_25\include\win32" -Wl,--add-stdcall-alias -Wl,--output-def,HelloWorld.def -shared -o HelloWorld.dll HelloWorld.c
echo run the program
java HelloWorld
The signature in the exception doesn't have the 'int' parameter. So your Java code disagrees with your native code.
This is my first attempt at JNI. My ultimate goal is to get all tasks currently running on a machine, but need to get even a simple example running. I keep getting this error when I try to execute my main program. I have supplied my simple Java main program, the header file generated, and the error.
I do not know what this DLL could be dependent on. It was initially referencing a DLL I tracked down and placed into system32 (msvcr90.dll).
Here is the command I used to compile the C code as well which produced the DLL, OBJ, LIB, EXP and manifest files.
cl -I"C:\Program Files\Java\jdk1.6.0\include" -I"C:\Program Files\Java\jdk1.6.0\include\win32" -MD -LD HelloWorld.c -FeHelloWorld.dll
class HelloWorld {
private native void print();
public static void main(String[] args) {
new HelloWorld().print();
}
static {
System.load("C:\\temp\\HelloWorld.dll");
}
}
#include <jni.h>
#include <stdio.h>
#include "HelloWorld.h"
JNIEXPORT void JNICALL
Java_HelloWorld_print(JNIEnv *env, jobject obj)
{
printf("Hello World!\n");
return;
}
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorld */
#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: HelloWorld
* Method: print
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_HelloWorld_print
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
java.lang.UnsatisfiedLinkError: C:\temp\HelloWorld.dll: A dynamic link library (DLL) initialization routine failed
at java.lang.ClassLoader$NativeLibrary.load(Native Method)
at java.lang.ClassLoader.loadLibrary0(Unknown Source)
at java.lang.ClassLoader.loadLibrary(Unknown Source)
at java.lang.Runtime.load0(Unknown Source)
at java.lang.System.load(Unknown Source)
at HelloWorld.<clinit>(HelloWorld.java:7)
Exception in thread "main"
The Unsatisfied Link Error can mean many things went wrong. I would use
System.loadLibrary("HelloWorld");
Instead of
System.load();
As TwentyMiles suggested.
Also, when invoking your program you need to (assuming your DLL is on the same directory as your class files:
java -Djava.library.path=. HelloWorld
Here's a simple demo I made that calls a Win32 API function (MessageBox)
Java class
class CallApi{
private native String showMessageBox(String msg);
private native double getRandomDouble();
static{
try{
System.loadLibrary("CallApi");
System.out.println("Loaded CallApi");
}catch(UnsatisfiedLinkError e){
//nothing to do
System.out.println("Couldn't load CallApi");
System.out.println(e.getMessage());
}
}
public static void main(String args[]){
CallApi api = new CallApi();
double randomNumber = api.getRandomDouble();
String retval = api.showMessageBox("Hello from Java!\n"+
"The native random number: "+randomNumber);
System.out.println("The native string: "+retval);
}
}
Generated header file
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class CallApi */
#ifndef _Included_CallApi
#define _Included_CallApi
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: CallApi
* Method: showMessageBox
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_CallApi_showMessageBox
(JNIEnv *, jobject, jstring);
/*
* Class: CallApi
* Method: getRandomDouble
* Signature: ()D
*/
JNIEXPORT jdouble JNICALL Java_CallApi_getRandomDouble
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
The C DLL code
#include "CallApi.h"
#include <windows.h>
#include <stdlib.h>
#include <time.h>
#pragma comment(lib,"user32.lib")
JNIEXPORT jstring JNICALL Java_CallApi_showMessageBox
(JNIEnv *env, jobject thisObject, jstring js)
{
//first convert jstring to const char for use in MessageBox
const jbyte* argvv = (*env)->GetStringUTFChars(env, js, NULL);
char* argv =(char *) argvv;
//Call MessageBoxA
MessageBox(NULL, argv, "Called from Java!", MB_ICONEXCLAMATION | MB_OK);
return js;
}
JNIEXPORT jdouble JNICALL Java_CallApi_getRandomDouble
(JNIEnv *env, jobject thisObject)
{
double num1;
srand((unsigned)(time(0)));
num1 = ((double)rand()/(double)RAND_MAX);
return num1;
}
Compile instructions
I compile with the Visual C++ express 2008 cl, removing the -ML flag since it causes an exception when the Java code tries to call the native code:
cl /I"c:\Program Files\Java\jdk1.6.0_10\include" /I"c:\Program Files\Java\jdk1.6.0_10\include\win32" -LD CallApi.c -FeCallApi.dll
Then, to run the code:
java -Djava.library.path=. CallApi
I'm not claiming to understand the situation enough to explain it, however some users reported the error when using the "-MD" compiler flag.
For more information see Java Native Interface (JNI) - Impossible to use VS2005 with Java? which discusses this problem and offers possible work-arounds and think techie blog for alternatives.
I believe that you should be using
System.loadLibrary("HelloWorld");
instead of System.load. LoadLibrary will check your system path (not the Java library path) so make sure that HelloWorld.dll is in a directory where it can found. Also note that it does not require the full path, and you don't need to add the dll extension to the end.
I just removed -MD option and compiled it worked like charm
cl -I"C:\Program Files\Java\jdk1.6.0_21\include" -I"C:\Program Files\Java\jdk1.6.0_21\include\win32" -LD HelloWorld.c -FeHelloWorld.dll
If you change the location (package) of your native function declaration from the java side without updating the h file, and the signature of the method in the c++ side, It won't resolve to the method and will throw unsatisfied..
package x;
public class A {
private native void print();
...
}
moved to:
package x.y;
public class A {
private native void print();
...
}
This will require regeneration of the H file (to something like Java_x_y_A_print ).
Note you can change those signatures manually but I won't recommend