JNI(Java Native Interface) https://docs.oracle.com/en/java/javase/19/docs/specs/jni/
User Code
|
Java JNI call
--------------------|---------------------
C/native JNI impl
|
Target Library
JNA(Java Native Library) https://github.com/java-native-access/jna
JNR(Java Native Runtime) FFI(Foreign Function Interface) https://github.com/jnr/jnr-ffi
User Code
|
JNA/JNR stub
|
Java JNI call
--------------------|---------------------
C/native JNI impl
|
libffi
|
Target Library
org.feuyeux.java.HelloJni.java
package org.feuyeux.java;
public class HelloJni {
// export LD_LIBRARY_PATH=...
static {
System.loadLibrary("hello");
}
private native String sayHello(String name);
public static void main(String[] args) {
String jniResult = new HelloJni().sayHello("JNI");
System.out.println("Java output:" + jniResult);
}
}# generate head
javac -h jni_lib src/main/java/org/feuyeux/java/HelloJni.java#include <jni.h>
#include <stdio.h>
#include <string.h>
#include "org_feuyeux_java_HelloJni.h"
// Implementation of the native method sayHello()
JNIEXPORT jstring JNICALL Java_org_feuyeux_java_HelloJni_sayHello(JNIEnv *env, jobject thisObj, jstring j_str)
{
const char *c_str = (*env)->GetStringUTFChars(env, j_str, NULL);
if (c_str == NULL) {
return NULL; // Out of memory
}
char cs[128] = "Hello ";
strncat(cs, c_str, sizeof(cs) - strlen(cs) - 1);
printf("C output: %s\n", cs);
(*env)->ReleaseStringUTFChars(env, j_str, c_str);
return (*env)->NewStringUTF(env, cs);
}libhello.dylib
mkdir jni_coon
gcc -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/darwin" -dynamiclib -o jni_coon/libhello.dylib jni_lib/hello_jni.clibhello.so
mkdir jni_coon
gcc -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" -shared -o jni_coon/libhello.so jni_lib/hello_jni.cjavac -cp ./jni_coon -d jni_lib src/main/java/org/feuyeux/java/HelloJni.javacd jni_lib
java -Djava.library.path=../jni_coon org.feuyeux.java.HelloJniC output:Hello JNI
Java output:Hello JNImkdir jna_lib
touch jna_lib/hello.chello.c
#include <stdio.h>
char* sayHello(char *name)
{
static char buffer[50];
snprintf(buffer, sizeof(buffer), "Hello %s", name);
printf("C output: %s\n", buffer);
return buffer;
}mkdir jna_coon
gcc -fPIC -shared -o jna_coon/libhello.dylib jna_lib/hello.cmkdir jna_coon
gcc -fPIC -shared -o jna_coon/libhello.so jna_lib/hello.cpackage org.feuyeux.java;
import com.sun.jna.Library;
import com.sun.jna.Native;
public class HelloJna {
public interface CLibrary extends Library {
// cp lib/libhello.dylib src/main/resources/
CLibrary clib = Native.load("hello", CLibrary.class);
String sayHello(String name);
}
public static void main(String[] args) {
String jnaResult = CLibrary.clib.sayHello("JNA");
System.out.println("Java output: " + jnaResult);
}
}# jna.version
# ls $HOME/.m2/repository/net/java/dev/jna/jna
export VERSION=5.15.0
export JNA_PATH=$HOME/.m2/repository/net/java/dev/jna/jna/$VERSION/jna-$VERSION.jar
javac -cp $JNA_PATH -d jna_lib src/main/java/org/feuyeux/java/HelloJna.javacd jna_lib
java -cp ./:$JNA_PATH:../jna_coon org.feuyeux.java.HelloJnaC output: Hello JNA
Java output: Hello JNApackage org.feuyeux.java;
import jnr.ffi.LibraryLoader;
public class HelloJnr {
public interface CLibrary {
String sayHello(String name);
}
public static void main(String[] args) {
// cp lib/libhello.dylib /usr/local/lib
CLibrary clib = LibraryLoader.create(CLibrary.class).load("hello");
String jnrFfi = clib.sayHello("JNR FFI");
System.out.println("Java output: " + jnrFfi);
}
}mvn dependency:tree |grep jnr -A 10
[INFO] \- com.github.jnr:jnr-ffi:jar:2.2.13:compile
[INFO] +- com.github.jnr:jffi:jar:1.3.10:compile
[INFO] +- com.github.jnr:jffi:jar:native:1.3.10:runtime
[INFO] +- org.ow2.asm:asm:jar:9.2:compile
[INFO] +- org.ow2.asm:asm-commons:jar:9.2:compile
[INFO] +- org.ow2.asm:asm-analysis:jar:9.2:compile
[INFO] +- org.ow2.asm:asm-tree:jar:9.2:compile
[INFO] +- org.ow2.asm:asm-util:jar:9.2:compile
[INFO] +- com.github.jnr:jnr-a64asm:jar:1.0.0:compile
[INFO] \- com.github.jnr:jnr-x86asm:jar:1.0.2:compile# jnr-ffi.version
export VERSION=2.2.13
export JFFI_VERSION=1.3.10
export ASM_VERSION=9.2
export JNR_PATH=$HOME/.m2/repository/com/github/jnr/jnr-ffi/$VERSION/jnr-ffi-$VERSION.jar
export JFFI_PATH=$HOME/.m2/repository/com/github/jnr/jffi/$JFFI_VERSION/jffi-$JFFI_VERSION.jar
export JFFI_NATIVE_PATH=$HOME/.m2/repository/com/github/jnr/jffi/$JFFI_VERSION/jffi-$JFFI_VERSION-native.jar
export ASM_PATH=$HOME/.m2/repository/org/ow2/asm/asm/$ASM_VERSION/asm-$ASM_VERSION.jarmkdir jnr_lib
javac -cp ./jna_coon:$JNR_PATH -d jnr_lib src/main/java/org/feuyeux/java/HelloJnr.javacd jnr_lib
java -cp ./:../jna_coon:$JNR_PATH:$JFFI_PATH:$JFFI_NATIVE_PATH:$ASM_PATH -Djava.library.path=../jna_coon org.feuyeux.java.HelloJnrC output: Hello JNR FFI
Java output: Hello JNR FFIexport jni_coon_path=/Users/hanl5/coding/feuyeux/hello-java-native/jni_coon
export JAVA_LIBRARY_PATH=$JAVA_LIBRARY_PATH:$jni_coon_path
mvn test -Dtest=org.feuyeux.java.HelloTest#testHelloJniexport jni_coon_path=/mnt/d/coding/hello-java-native/jni_coon
export LD_LIBRARY_PATH=$jni_coon_path:$LD_LIBRARY_PATH
mvn test -Dtest=org.feuyeux.java.HelloTest#testHelloJnimvn test -Dtest=org.feuyeux.java.HelloTest#testHelloJnamvn test -Dtest=org.feuyeux.java.HelloTest#testHelloJnrexport jni_coon_path=/Users/hanl5/coding/feuyeux/hello-java-native/jni_coon
export JAVA_LIBRARY_PATH=$JAVA_LIBRARY_PATH:$jni_coon_path
mvn test -Dtest=org.feuyeux.java.benchmark.HelloBenchmarkexport jni_coon_path=/mnt/d/coding/hello-java-native/jni_coon
export LD_LIBRARY_PATH=$jni_coon_path:$LD_LIBRARY_PATH
mvn test -Dtest=org.feuyeux.java.benchmark.HelloBenchmark