I'm trying to use JNI to access C++ methods from a Java class. I'm able to compile (both in Eclipse or on command line) my Java class fine, but on executing the class at runtime, I'm getting:
Exception in thread "main" java.lang.UnsatisfiedLinkError: com.domain.services.CallServiceAPIS.createSession()I
at com.domain.services.CallServiceAPIS.createSession(Native Method)
at com.domain.services.CallServiceAPIS.main(CallServiceAPIS.java:18)
Java code is as follows:
package com.domain.services;
public class CallServiceAPIS {
static {
System.loadLibrary("service.client");
}
public native int createSession();
public static void main(String[] args) {
System.out.println(System.getProperty("java.library.path"));
new CallServiceAPIS().createSession();
}
}
I included the printout of the java.library.path just to make sure it's pointing to the correct location of the C++ library - and it is. I also tried setting the LD_LIBRARY_PATH in my Eclipse environment. But neither worked.
Note that the System.loadLibrary call IS working since 1) the code compiles and 2) the error occurs on line 18, which is the new CallServiceAPIs call.
C++ code:
int createSession(const PosServiceInfo info, const SessionArgs& args, Domain::UUID& uuidSession)
{
return int::undefined;
}
Any ideas?
Never mind. I realized that I was using the JNI interface incorrectly. I was thinking you could load an EXISTING C++ library using EXISTING C++ source. But you basically have to rewrite the existing code to make use of the JNI interface.
Related
So I have followed this Swig tutorial to generate JNI proxy class and shared library. In result I have got libexmple.so file.
In Building a Java module tutorial section you can see.
$ swig -java example.i
$ gcc -c example.c example_wrap.c -I/c/jdk1.3.1/include -I/c/jdk1.3.1/include/win32
$ gcc -shared example.o example_wrap.o -mno-cygwin -Wl,--add-stdcall-alias -o example.dll
$ cat main.java
public class main {
public static void main(String argv[]) {
System.loadLibrary("example");
System.out.println(example.getMy_variable());
System.out.println(example.fact(5));
System.out.println(example.get_time());
}
}
$ javac main.java
$ java main
3.0
120
Mon Mar 4 18:20:31 2002
3.0, 120 and Mon Mar 4 18:20:31 2002 are functions results.
Honestly I didnt even expected it to compile, but it did and it runs without problem when i do java main.
So my 1 question is? How does java know what "example" is in println method call
System.out.println(example.getMy_variable());
when I try to compile it thourgh Intellij IDE, naturaly it throws me an error
"could not find symbol example" as i should throw.
2ndly when i try to use this library in this way
public class Main {
static {
try {
System.loadLibrary("example");
System.out.println("lib initialized");
} catch (Exception e) {
System.out.println(e.toString());
}
}
public static native void My_variable_set(double jarg1);
public static native double My_variable_get();
public static native int fact(int jarg1);
public static native int my_mod(int jarg1, int jarg2);
public static native String get_time();
public static void main(String args[]) {
try {
Main.fact(3);
System.out.println("method called successfully");
} catch (Exception e) {
System.out.println(e.toString());
}
}
i am getting
Exception in thread "main" java.lang.UnsatisfiedLinkError: test.Main.fact(I)I
when trying to call native method.
I am sure that lib is loaded properly (getting lib initialized log). When I tried to load non existence i got different error (class load exception)
java.lang.UnsatisfiedLinkError: no exampleld in java.library.path
Why does it even compile?
Because javac can see that main uses class example from the same package, and compiles it. Actually, it finds also the class exampleJNI, which is also auto-generated by swig, and is used in example.java.
How does java know what "example" is in println method call?
In Java, you don't need import for classes that are in the same package, that's how java knows to call example.getMy_variable().
when i try to use this library, I get exception
Your attempt to move the native methods from exampleJNI.java to main.java failed because with JNI, the name of the class is used to determine the name of the native function. You can see it if you look at the list of exported functions of example.dll.
If you want to use swig, just use the files it generated. If you want to understand better how JNI works, read the books or online documents, follow tutorials, etc. Don't try to use swig for what it is not: an introduction to JNI programming.
I have built a DLL which I am attempting to wrap Java code with, however I am having some troubles with running my Java program. I wrote a simple test DLL and Java program and am producing the same error, and although there are plenty of resources regarding NoClassDefFoundError online I can't seem to solve mine with any troubleshooting methods.
Here is my D:\Test1.Java file
public class Test1 {
static {
//System.loadLibrary("HeyLand");
System.load("D://HeyLand.dll");
}
public native void displayHeyLand();
public static void main (String[] args) {
Test1 t = new Test1();
t.displayHeyLand();
}
}
After compiling, attempting to run D:\Test1.classresults in the following:
D:\>java Test1.class
Exception in thread "main" java.lang.NoClassDefFoundError: Test1.class
Caused by: java.lang.ClassNotFoundException: Test1.class
at java.net.URLClassLoader.findClass(URLClassLoader.java:434)
at java.lang.ClassLoader.loadClass(ClassLoader.java:660)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:358)
at java.lang.ClassLoader.loadClass(ClassLoader.java:626)
Could not find the main class: Test1.class. Program will exit.
Why I am stumped :
1. I have set my classpath to be D:\, so I believe my class definition would be in the classpath, and I do not see how my compile-time and run-time classpaths could be any different.
2. I don't see how this could have anything to do with static initialization, and I believe the exception would look different.
Perhaps I'm just missing something incredibly simple, I am very newbie with Java.
Any help is greatly appreciated!
The classpath environmental variable is taking precedence over that in the java run command. You need to specify the class location (as well as removing the .class file extension)
java -cp . Test1
Java normal syntax for executing class file is
Java [<options>....} <class-name> [<arguments>....]
For example
java com.package.name.Test1
here how compiler works
1. Compiler search for complete class name
2. Load that class
3. check for main method - in the same class
4. Call main method with passed arguments in command line string.
Now following are the possibilities why your class may not found main method.
1 - forgot to include package name
I am new developer in java but I found when I run application using eclips or intellJ editor it gives different path and package name and execute code as I noticed it on command line edior. So make sure you are including package name
For example:
java com.package.name.Test1 instead of
java Test1
2. File name or pathname rather then class name
As I noticed output file is in different location. That why class file path was different.
java Test1.class
java com/package/name/Test1.class
3. Typo
also I noticed you are using
static {
//System.loadLibrary("HeyLand");
System.load("D://HeyLand.dll");
}
Is this function ? or constructor? If it is function then where is name of the function? You cant write code without any reference in classs
I want to create c library and use it in my java code on an Linux OS. I'm trying to understand and implement natural library concept.
I'm following this tutorial
http://diglib.stanford.edu:8091/~testbed/doc/JavaUsage/JNI/tutorial.txt
Which is helpful me to understand concept a little. However, I get errors when I try to do it myself. I searced for errors I am getting but none of solutions helped.
Main class code and class for natural library I wrote is as follows:
package natLib;
import natLib.getKeyPressed;
public class main {
public static void main(String[] args) {
getKeyPressed natlab=new getKeyPressed();
char c=natlab.keyboardPressedKey();
}
}
package natLib;
public class getKeyPressed {
static {
System.loadLibrary("natlab");
}
public native char keyboardPressedKey();
}
when I write "javac main.java"
I get errors like
"main.java:6: error: cannot find symbol
getKeyPressed natlab=new getKeyPressed();"
And when I skip for main and just do javac prcess for class with native method, try to obtain a header file
javah -jni getKeyPressed.class
Although there is a file as getKeyPressed.class, I get errors like:
"Exception in thread "main" java.lang.IllegalArgumentException: Not a valid class name: getKeyPressed.class"
I try it without .class extention it says
"Error: Could not find class file for 'getKeyPressed'."
It says that even when I make getKeyPressed class file by copying getKeyPressed.class.
It seems I am making a major mistake, any suggestions to solve this?
javah expects a fully qualified classname. (e.g. natLib.getKeyPressed, not just getKeyPressed)
I have to call a dll method and I don't have the source code from dll, I was reading about JNI and understood that you should have the source to input the JNI library in the code (.h).
My second shoot is JNA, but I am getting the same error, although you don't have to change anything in DLL.
I created two classes to test:
interface:
package icom;
import com.sun.jna.Library;
public interface IConectorT extends Library {
int StartConector(byte[] conectorStatus, String icomPath);
}
DLL method call:
package icom;
import com.sun.jna.Native;
public class ConectorTJna {
public static void main(String args[]) {
IConectorT lib = (IConectorT) Native.loadLibrary("ConectorT", IConectorT.class);
int teste = lib.StartConector(null, "C:\\ICOM");
System.out.println("RESULT: " + teste);
}
}
When I call the lib.StartConector method I get this:
Exception in thread "main" java.lang.UnsatisfiedLinkError: Error
looking up function 'StartConector': The specified procedure could not
be found. at com.sun.jna.Function.(Function.java:179) at
com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:350) at
com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:330) at
com.sun.jna.Library$Handler.invoke(Library.java:203) at
$Proxy0.StartConector(Unknown Source) at
icom.ConectorTJna.main(ConectorTJna.java:10)
Did you specify path to the library, e.g. using system property?
Here are the details from "Getting Started with JNA" guide:
Make your target library available to your Java program. There are two
ways to do this:
The preferred method is to set the jna.library.path system property to
the path to your target library. This property is similar to
java.library.path, but only applies to libraries loaded by JNA.
Change the appropriate library access environment variable before launching
the VM. This is PATH on Windows, LD_LIBRARY_PATH on Linux, and
DYLD_LIBRARY_PATH on OSX.
Taken from: https://github.com/twall/jna/blob/master/www/GettingStarted.md
I have written JNI wrappers to export the API of a C application (G-WAN) which embeds a JVM. The native calls are implemented in the C application and exported with RegisterNatives().
Ideally I would have a 'gwan' class for the G-WAN API:
import gwan // G-WAN API
public class hello {
public static int jmain(long env, String[] args) {
gwan.xbuf_cat(gwan.get_reply(env), "Hello World");
return 200; // HTTP status (200:'OK')
}
}
I would like to do something like the "#import gwan" above to import the native call prototypes, but currently I only have the following (which works):
public class hello {
public static int jmain(long env, String[] args) {
gwan_xbuf_cat(gwan_get_reply(env), "Hello World");
return 200; // HTTP status (200:'OK')
}
public static native long gwan_get_reply(long env);
public static native void gwan_xbuf_cat(long ctx, String str);
}
Again, the implementation of the native calls in made in the G-WAN executable (not in a Java class stored on disk).
Because the G-WAN API is quite large, I would like to have the native call prototypes in their own 'gwan' class (or namespace) if possible (like in the first hello example above).
Any suggestion about how to do that?
(please post Java or JNI code because I am not a Java expert)
Disclamer: I am involved in the development of this project.
I would suggest that you read following paper on JNI from Sun now Oracle
http://java.sun.com/docs/books/jni/html/jniTOC.html
And after that it should be understandable but some pseudocode and its not tested would be to move the two gwanapi calls into its own file named gwanapi.java
public class gwanapi {
public static native long get_reply(long answer);
public static native void xbuf_cat(long ctx,String str);
}
then you compile that file with javac gwanapi.java -> output: gwanapi.class
you type javah -jni for the c/c++ header implementation:
javah -jni gwanapi
the next you should in your hello java class is to call static{ System.loadLibrary("gwanapi");}
Pseudo code and NOT tested
public class hello{
static{
System.loadLibrary("gwanapi");
}
public static int jmain(long env,String args[]){
gwanapi.xbuf_cat(gwanapi.get_reply(env),"Hello World!");
return 200;
}
}
and you should be good to go.
But I might have missed a point or two but I think this is the smallest amount of work you should do.
Oh by the way http://en.wikipedia.org/wiki/Java_Native_Interface is also some form of source for JNI calls and how it works and lead you to more sites with more information.
Thanks
Being a C programmer, I have had to read the C source code of the JVM to find that Java remaps Class (path) names with dots instead of slashes.
Since the G-WAN directory hierarchy uses IP addresses to define listeners and virtual hosts (192.168.10.10_80/#domain.com/csp), those dots were confusing the FindClass() JNI call, making it fail to find the classes.
I also found that the classpath path separator is a ":" for Unix and a ";" for Windows. That was not the cause of my problem, but it might cause the same issue.
Finally, I stopped using the GCJ compiler's JVM because it does not support formating doubles (since at least 2006). Using either OpenJDK or the SUN/ORACLE JVM works as expected.
All works fine now. I post all this here just in case it may help others.