# JNI short tutorial

#### 1 Write a Java class that uses C codes

{% code title="CraftJNI.java" %}

```java
public class CraftJNI {
  static {
    System.loadLibrary("hello"); // Load native library hello.dll (windows)
                                 // or libhello.so (Unixes) at runtime.
                                 // This library contains a native method sayHello()
                                 
  }
  
  // Declare an instance native method sayHello() which receives no parameter
  // returns void
  private native void sayHello();
  
  // Test Driver
  public static void main(String[] args) {
    new CraftJNI().sayHello(); // Create an instance and invoke native method
  }
}
```

{% endcode %}

#### 2 Compile Java program `CraftJNI.java`

```bash
javac -h . CraftJNI.java
```

{% code title="CraftJNI.h" %}

```bash
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class CraftJNI */

#ifndef _Included_CraftJNI
#define _Included_CraftJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     CraftJNI
 * Method:    sayHello
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_CraftJNI_sayHello
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif
```

{% endcode %}

To know more about the generated class file

```bash
javap -verbose CraftJNI.class
```

#### 3 Implement C program

{% code title="CraftJNI.c" %}

```c
#include <jni.h>   // Standard JDK provided header
#include <stdio.h> // Standard C IO header
#include <CraftJNI.h> // Generated header

JNIEXPORT void JNICALL Java_CraftJNI_sayHello(JNIEnv *env, jobject thisObj) {
  printf("I am in C code \n");
  return;
}

```

{% endcode %}

#### 4 Compile C program

```bash
gcc -fPIC -I"." -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" \
   -shared -o libhello.so CraftJNI.c
```

```bash
> java -Djava.library.path=. CraftJNI
I am in C code
```

### Troubleshooting

#### Error 1: class file has wrong version

```bash
> javah CraftJNI.class
Error: cannot access CraftJNI
  bad class file: ./CraftJNI.class
    class file has wrong version 55.0, should be 52.0
```

Reason: `javah` is not available with Java 11

```bash
> java -version
openjdk version "11.0.9.1" 2020-11-04
OpenJDK Runtime Environment (build 11.0.9.1+1-post-Debian-1deb10u2)
OpenJDK 64-Bit Server VM (build 11.0.9.1+1-post-Debian-1deb10u2, mixed mode, sharing)

> javac -version
javac 11.0.9.1

> javah -version
javah version "1.8.0_252"

> whereis java
java: /usr/bin/java /usr/share/java /usr/share/man/man1/java.1.gz

> ls /usr/share/man/man1 | grep java*
java.1.gz
javac.1.gz
javadoc.1.gz
javah.1.gz
javap.1.gz
```

#### Error 2:

```bash
> java -Djava.library.path=. CraftJNI
Exception in thread "main" java.lang.UnsatisfiedLinkError: no hello in java.library.path: [.]
        at java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2670)
        at java.base/java.lang.Runtime.loadLibrary0(Runtime.java:830)
        at java.base/java.lang.System.loadLibrary(System.java:1873)
        at CraftJNI.<clinit>(CraftJNI.java:4)
```

### References

1. JNI reference docs - <https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/jniTOC.html>
2. Some of the code is shamelessly copied from <https://www3.ntu.edu.sg/home/ehchua/programming/java/JavaNativeInterface.html>
