I am building a small app on a raspberry pi.
I have a JVM which tries to access a C++ Library called "RCSwitch"
I created a JavaClass
public class NativeRCSwitchAdapter {
private static final NativeRCSwitchAdapter instance = new NativeRCSwitchAdapter();
public static NativeRCSwitchAdapter getInstance(){
return instance;
}
private NativeRCSwitchAdapter(){};
static{
String path = NativeRCSwitchAdapter.class.getProtectionDomain().getCodeSource().getLocation().getPath();
System.load(path + "NativeRCSwitchAdapter.so");
}
// methods to redirect to native layer (C++)
public native void switchOn(String group, String channel);
public native void switchOff(String group, String channel);
}
I then ran javac & javah to have java generate my header file for me.
I created a c++ file:
#include "NativeRCSwitchAdapter.h"
#include "RCSwitch.h"
#include <stdio.h>
#include <iostream>
using namespace std;
JNIEXPORT void JNICALL Java_NativeRCSwitchAdapter_switchOn(JNIEnv * env, jobject obj, jstring jsGroup, jstring jsChannel ){
cout<<"teststring output"<<endl;
const char *csGroup = env->GetStringUTFChars(jsGroup, 0);
const char *csChannel = env->GetStringUTFChars(jsChannel, 0);
char sGroup[6];
char sChannel[6];
for (int i = 0; i<5; i++) {
sGroup[i] = csGroup[i];
sChannel[i] = csChannel[i];
}
sGroup[5] = '\0';
sChannel[5] = '\0';
cout<<"ONON"<<endl;
cout<<sGroup<<endl;
cout<<sChannel<<endl;
RCSwitch mySwitch = RCSwitch();
//for testing purposes set to the ELRO Power Plugs
mySwitch.setPulseLength(300);
mySwitch.enableTransmit(0);
mySwitch.setRepeatTransmit(3);
mySwitch.switchOn(sGroup, sChannel);
}
Now this file uses the RCSwitch library which in turn uses the wiringPi library.
Now if i compile i run this:
g++ -shared -I/usr/jdk1.8.0/include -I/usr/jdk1.8.0/include/linux NativeRCSwitchAdapter.cpp -o NativeRCSwitchAdapter.so
Yet I get this error if start everything from java: (simple main, create an instance of my object and run the switchOn()
java: symbol lookup error: /home/pi/applications/Pi-jAutomation433/RCSwitchJNIWrapper/src/NativeRCSwitchAdapter.so: undefined symbol: _ZN8RCSwitchC1Ev
It has been time, since i last coded in C, so please forgive me but I believe it has something to do with the the linking phase of the compiler? Or does the compiler automatically check all dependencies and then their deps until no further dependencies are found and it then links it all nicely together and wraps it in an app?
Oh here is the repo to have an in depth look if anybody cares:
Github repo
Thanks for any help coming my way!
UPDATE
Okay so I managed to get this error away. Turns out (well I kinda knew that already but yeah) I am quiet a duphus when it comes to C++ compiler knowledge. Anyways I managed to get the error changed. I didn't know I had to explicitly tell g++ to include RCSwitch.cpp as well. Okay so now I did. Next error ;-)
I guess this time it should be fairly easy to tackle. I get an undefined symbol "pinMode".
This symbol is part of the wiringPi library. Do I have to include ALL c librarys that are executed in my java file? Or only the one I access and anything after that doesnt matter to java?
Your native function declaration is getting mangled by the c++ compiler. Add extern "C" around your declarations to clear up the issue.
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
JNIEXPORT void JNICALL Java_NativeRCSwitchAdapter_switchOn(JNIEnv * env, jobject obj, jstring jsGroup, jstring jsChannel ){
#ifdef __cplusplus
}
#endif /* __cplusplus */
Edit:
You need to include all other objects/libraries into your creation of the shared library.
See this Dynamic Link Library Q/A.
Related
I want to call this minimal dummy C program (named "TEST.c"):
extern "C"
void Java_TEST_run() {}
from this Java code (named "Example.java"):
public class Example
{
public static void main(String args[])
{
System.out.println("START");
TEST test = new TEST();
test.dll_call();
System.out.println("ALL DONE!");
}
}
class TEST
{
public void dll_call()
{
run();
}
static {
try {
System.out.println("Load DLL = start ");
System.load("/home/user/Desktop/TEST.dll");
System.out.println("Load DLL = finish ");
} catch (UnsatisfiedLinkError e) {
System.err.println("Native code library failed to load.\n");
System.exit(1);
}
}
public native void run();
}
I create the C dll file by the following commands:
g++ -c TEST.c
g++ -shared -o TEST.dll TEST.o
This works all fine within the console environment, esp. I got the successful Java program execution output:
START
Load DLL = start
Load DLL = finish
ALL DONE!
Now, if I try to run the Java program from the Eclipse IDE, I got the following error:
START
Load DLL = start
Load DLL = finish
Exception in thread "main" java.lang.UnsatisfiedLinkError: 'void Example.TEST.run()'
at test1/Example.TEST.run(Native Method)
at test1/Example.TEST.dll_call(Example.java:21)
at test1/Example.Example.main(Example.java:11)
To my understanding, this means the Java program running from Eclipse does successfully find the c dll file, but when trying to enter the dll file, it fails by finding the dummy function Java_TEST_run().
As the c code "TEST.c" is already as minimal as possible, and as the execution works fine from the console, I do not understand, why it fails from Eclipse.
Can someone please advice me, how to make this minimal c code working from Java in Eclipse?
Operating system is Ubuntu 18.04 with openjdk 11.0.6 and Eclipse 4.14.0.
This is a follow up question from this question.
Any help much appreciated, thank you!
I was able to resolve the issue with Eclipse.
The C/C++ code needs to include the name of the Eclipse project, e.g. "Example", in the function name. In above code, this means:
extern "C"
void Java_TEST_run() {}
needs to be changed to:
extern "C"
void Java_Example_TEST_run() {}
I'll give you quick answer
First of all you will need JNI for c/c++ and it will generate header for your .java file.
For example, if we have class from your example we will have header output like this
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class TEST */
#ifndef _Included_TEST
#define _Included_TEST
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: TEST
* Method: run
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_TEST_run
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
You can generate this type of header by typing
javac -h . TEST.java for your given class
Last thing we have to do is create our .cpp file to add function body, you can just copy decleration from JNI generated header file
#include "TEST.h"
JNIEXPORT void JNICALL Java_TEST_run(JNIEnv * env, jobject obj){
//my very special code
}
And that would be it.
In order to add JNI to your Visual Studio (if you are using one) you will have to go to project properties and add additional include and additional library from jdk folder.
If you are using g++, add include directory of JDK and library.
I'm doing a tutorial about JNI to get comfortable with it for my project. However, I'm stuck on this particular part of the tutorial which is running the Java program. I'm using Eclipse where I've created a Java project and put the files inside a package. When I try to follow the tutorial without using Eclipse, I get no errors, so I assume it's a path related issue.
As for my error, I get an
UnsatisfiedLinkError: no libhello in java.library.path
when I run this in my terminal:
java -Djava.library.path=. helloJNI.HelloJNI
Even when I specify my path, it doesn't work. If you want to reproduce my steps, here's what I did:
After creating HelloJNI.java, I ran the following in the terminal:
javac -h . HelloJNI.java
This command created the generated header file given below.
After this, I wrote HelloJNI.c, navigated to the package folder and then compiled it using this command in the same directory:
gcc -fPIC -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" -shared -o libhello.so HelloJNI.c
Then I moved up one directory to ~/eclipse-workspace/HelloJNI/src and ran this command:
java -Djava.library.path=. HelloJNI
Which yields the error described above.
Here's my code:
HelloJNI.java
package helloJNI;
public class HelloJNI { // Save as HelloJNI.java
static {
System.loadLibrary("hello"); // Load native library hello.dll (Windows) or libhello.so (Unixes)
// at runtime
// This library contains a native method called sayHello()
}
// Declare an instance native method sayHello() which receives no parameter and returns void
private native void sayHello();
// Test Driver
public static void main(String[] args) {
new HelloJNI().sayHello(); // Create an instance and invoke the native method
}
}
HelloJNI.c
// Save as "HelloJNI.c"
#include <jni.h> // JNI header provided by JDK
#include <stdio.h> // C Standard IO Header
#include "helloJNI_HelloJNI.h" // Generated
// Implementation of the native method sayHello()
JNIEXPORT void JNICALL Java_HelloJNI_sayHello(JNIEnv *env, jobject thisObj) {
printf("Hello World!\n");
return;
}
helloJNI_HelloJNI.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class helloJNI_HelloJNI */
#ifndef _Included_helloJNI_HelloJNI
#define _Included_helloJNI_HelloJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: helloJNI_HelloJNI
* Method: sayHello
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_helloJNI_HelloJNI_sayHello
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
I have also tried setting the path to my working directory, but I still get the same result. What am I doing wrong?
I moved up one directory to ~/eclipse-workspace/HelloJNI/src
This means that your libhello.so is in ~/eclipse-workspace/HelloJNI/src/helloJNI directory, isn't it?
Now, to let Java find this library, run
java -Djava.library.path=helloJNI helloJNI.HelloJNI
or use the absolute path
java -Djava.library.path=~/eclipse-workspace/HelloJNI/src/helloJNI helloJNI.HelloJNI
I have a library say libraryOne.so (File1.c) which contains pure C code.
Now I want to access this code from my Java file. For this I shall use the standard JNI procedure.
But for using JNI, I should also modify the C code inside my File1.c (like including the header files for JNI and some standard JNIEXPORT stuff).
So I am creating a wrapper library (libraryWrapper.so made from say File2.c). This library will contain the required JNI declarations and shall interact with my Java file.
Now I want to know how can I call the pure C functions of File1.c from File2.c
Firstly, you should read ndk-tutorial. Cause you need to setup ndk and so on. And after you can look for ndk samples to learn how to do things.And how to build
Actually Above is your answer.
Coming to your question.
You should have source code of libraryOne.so. AS you say you have. Cause android target many platforms on which your so can not be run .so it need to be compiled with ndk
OK lets be our file File1.c
int myfunction(const char* st){
int answer=0;
//stufs here
return answer;
}
Firstly, Add Java class from which we want to access
package example.com.myapplication;
public class JniClass {
static {
System.loadLibrary("libraryWrapper");
}
public native int myFunction(String a );
}
Now we need wrapper. For this we should know jni interface. Fortunately there is tool called javah which can generate it for us and save us from doing it manually for all our calls.
We should build our android project so that class files be generated.
I build it with debug so my generated files will be on this /app/build/intermediates/classes/debug folder.
Ok then we run javah
javah -jni example.com.myapplication.JniClass
if you got erros see Javah error while using it in JNI
It generated header file called example_com_myapplication_JniClass.h which contains
#include <jni.h>
/* Header for class example_com_myapplication_JniClass */
#ifndef _Included_example_com_myapplication_JniClass
#define _Included_example_com_myapplication_JniClass
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: example_com_myapplication_JniClass
* Method: myFunction
* Signature: (Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_example_com_myapplication_JniClass_myFunction
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
OK now your answer.
Easy way will be to to copy generated code and paste it on the bottom of File1.c code. and implementing it:
Note that I named arguments env,obj and str after pasting it and cleaned comments
//..here your File1.c source code content
//
#include <jni.h>
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jint JNICALL Java_example_com_myapplication_JniClass_myFunction
(JNIEnv * env, jobject obj, jstring str){
const char * x=(*env)->GetStringUTFChars(env,str,0);
//call our function
int ret= myfunction(x);
(*env)->ReleaseStringUTFChars(env,str, x);
return ret;
}
#ifdef __cplusplus
}
#endif
But actually you might use header files on your c code. for example you could add generated header file and implement it on .c file. And on .c file you could include your File1.c header. And this way it be more clean way.
Assuming you already read ndk tutorial.And you setup ndk tools and know how to build
And finally we add File1.c to jni folder and change Android.mk file
LOCAL_MODULE := libraryWrapper
LOCAL_SRC_FILES := File1.c
and
APP_MODULES := libraryWrapper
then we build it with ndk-build which will add our so files to libs
Op wanted only with File1.c and File2.c
inside File2.c
//declaration of funtions inside File1.c
extern int myfunction(const char* st);
#include <jni.h>
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jint JNICALL Java_example_com_myapplication_JniClass_myFunction
(JNIEnv * env, jobject obj, jstring str){
const char * x=(*env)->GetStringUTFChars(env,str,0);
//call our function
int ret= myfunction(x);
(*env)->ReleaseStringUTFChars(env,str, x);
return ret;
}
#ifdef __cplusplus
}
#endif
And change Android.mk
LOCAL_SRC_FILES := File1.c
LOCAL_SRC_FILES += File2.c
I am trying to use a package in my final year project called libfprint. This is an opensource fingerprint reader SDK. I am doing my project in Java so I need to port over the libfprint functionality.
A stroke of good luck hit me and turned out somebody already did this. A package called jlibfprint is a JNI wrapper for libfprint.
So I followed the instructions in both jlibfprint and libfprint for setup. libfprint more or less works fine. As for jlibfprint, when I tried to run the sample program I got,
Exception in thread "main" java.lang.UnsatisfiedLinkError: no JlibFprint_jni in java.library.path
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1681)
at java.lang.Runtime.loadLibrary0(Runtime.java:840)
at java.lang.System.loadLibrary(System.java:1047)
at JlibFprint.<clinit>(JlibFprint.java:28)
at SampleRun.main(SampleRun.java:30)
JlibFprint.(JlibFprint.java:28)
is referring to
static {
System.loadLibrary("JlibFprint_jni");
}
So now I'm looking through the project properties and get to the field "Native library location", and I point it to the directory containing a single file called libJlibFprint_jni.so.
Now when I run the program, the error I get is,
Exception in thread "main" java.lang.UnsatisfiedLinkError: JlibFprint.enroll_finger()LJlibFprint$fp_print_data;
at JlibFprint.enroll_finger(Native Method)
at SampleRun.main(SampleRun.java:36)
Enroll the first finger...
Here are the sample Java file
SampleRun.java
public class SampleRun {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
JlibFprint jlibfprint = new JlibFprint();
JlibFprint.fp_print_data pd1, pd2;
int matchValue;
try
{
System.out.println("Enroll the first finger...");
pd1 = jlibfprint.enroll_finger();
System.out.println("Compare the previous acquisition with the next one...");
pd2 = jlibfprint.enroll_finger();
matchValue = JlibFprint.img_compare_print_data(pd1, pd2);
System.out.println(matchValue);
if (matchValue > JlibFprint.BOZORTH_THRESHOLD)
{
System.out.println("[OK] The two fingerprints are compatible!");
}
else
{
System.out.println("[FAIL] The two fingerprints are not compatible!");
}
}
catch (JlibFprint.EnrollException e)
{
System.err.format("Enroll Exception [%d]\n", e.enroll_exception);
e.printStackTrace();
}
}
}
I am using Ubuntu 11.10 with Eclipse Juno.
Anybody with a breeze of knowledge in this area would be a great help !
I know this is a bit late. But if you are using the eclipse for your java project then go to the Run Configurations and then go to the Arguments tab.
In the VM arguments add following line:
-Djava.library.path="${workspace_loc}/PROJECT_NAME/DIRECTORY_NAME_CONTAINIG_LIBRARY:${env_var:PATH}"
Just found the solution here. I'm sure it was obvious to some but JNI is totally new to me. The solution was:
"Create a new file in /etc/ld.so.conf.d/ called .conf
Edit the file and add a line per directory of shared libraries (*.so
files), it will look something like:
/usr/lib/APPLICATION/lib Reload the list of system-wide library paths:
sudo ldconfig"
I'm just gonna throw a few JNI related answers, feel free to downvote.
Android Java JNI and C Char array Can't recognize the value
The call needs to look like
JNIEXPORT <return type> JNICALL Java_<your_package_name>_<classname>_<methodname>(JNIEnv *env, jobject obj, ...)
so lets try it out for an example that is
package com.rtrk.demo;
public class PELib
{
public native int play(String file, String file2, blahblah);
/* ... */
}
which should look like
JNIEXPORT jint JNICALL Java_com_rtrk_demo_PELib_play(JNIEnv *env, jobject obj, jstring main_video, jstring prev_video, jint main_x, jint main_y, jint main_width, jint main_height, jint prev_x, jint prev_y, jint prev_width, jint prev_height)
This is the main root cause for UnsatisfiedLinkErrors :)
I'm getting UnsatisfiedLinkError when invoking C functions from JNI though my setup seems correct. Here's what I've done:
There's a Java class:
package com.mycompany.myproduct;
public class Foo {
static {
System.loadLibrary("external");
}
public void native do_foo();
}
I've placed libexternal.so to the LD_LIBRARY_PATH, compiled the class, and executed javah over it. Resulting com_mycompany_myproduct_Foo.h file:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_mycompany_myproduct_Foo */
#ifndef _Included_com_mycompany_myproduct_Foo
#define _Included_com_mycompany_myproduct_Foo
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_com_mycompany_myproduct_Foo
* Method: do_foo
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_mycompany_myproduct_Foo_do_1foo(JNIEnv *, jobject);
Implemented a C delegation in ctinative.c (not sure if extern "C" is needed there):
#include "com_mycompany_myproduct_Foo.h"
#include "External.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_com_mycompany_myproduct_Foo
* Method: do_foo
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_mycompany_myproduct_Foo_do_1foo(JNIEnv *, jobject) {
do_foo(); // this is a function that defined in External.h
}
#ifdef __cplusplus
}
#endif
Compiled that and got ctinative.o:
gcc -x c -g -m64 -DUNIX=1 -DUSE_SBUF=1 -DMAIN_VERSION=0 -DC_VER=7 -I$(EXTERNAL_SDK_ROOT)/include -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux -o ctinative.o -c ctinative.c
Here's the output of nm ctinative.o (is having U there normal?):
0000000000000000 T Java_com_mycompany_myproduct_Foo_do_1foo
U do_foo
Placed that ctinative.o to LD_LIBRARY_PATH. Now when invoking Foo.do_foo() I'm getting UnsatisfiedLinkError:
java.lang.UnsatisfiedLinkError: com.mycompany.myproduct.Foo.do_foo()V
at com.mycompany.myproduct.Foo.do_foo(Native Method)
If I remove ctinative.o from LD_LIBRARY_PATH the error does not change. If I remove libexternal.so from LD_LIBRARY_PATH then of course I'm getting:
java.lang.UnsatisfiedLinkError: no external in java.library.path
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1734)
at java.lang.Runtime.loadLibrary0(Runtime.java:823)
at java.lang.System.loadLibrary(System.java:1028)
at com.mycompany.myproduct.Foo.<clinit>
Any idea on what I'm doing wrong?
OK, my experience with native libraries on Linux is limited to toy tests, however I have used them pretty extensively on Windows. I expect the mechanism is similar, but proceed with caution :)
Java ends up calling the Java_com_mycompany_myproduct_Foo_do_1foo() native function when you execute the method fooInstance.do_foo(). This is the native function that needs to be defined in libexternal.so (or whatever you choose to load with loadLibrary()).
If I understand your question correctly, you have compiled the function Java_com_mycompany_myproduct_Foo_do_1foo() into ctinative.o, and the implementation does not appear in libexternal.so. You can check this with objdump --dynamic-reloc libexternal.so.
I believe you need to have your native implementation of Java_com_mycompany_myproduct_Foo_do_1foo() compiled into libexternal.so, or alternatively you could link ctinative.o to produce a dynamic link library something like libctinative.so.
EDIT: To join the dots, the complete mechanism would be:
Your java code calls loadLibrary() on a .so file that implements the function Java_com_mycompany_myproduct_Foo_do_1foo(). Let's call this libctinative.so.
libctinative.so dynamically loads libexternal.so through the O/S's dynamic linking mechanism --- you don't need to do anything special to make this happen apart from compiling and linking libctinative.so in the right way
Your program runs correctly, assuming no other issues :)
You have Java_com_mycompany_myproduct_Foo_do_1foo() but native void do_foo(). Was do_1foo() its name when you generated the .h/.c files? If you've changed it you have to regenerate.