Call Java library from C with JNI

C using JNI in connection with Using Go to call Java libraries that I talked about at TechTalk. I will explain how to call Java from.

Java Native Interface(JNI)

JNI is a mechanism that allows you to use Java libraries from C and vice versa. The official documentation can be found in the Java Native Interface Specification Table of Contents (https://docs.oracle.com/javase/jp/8/docs/technotes/guides/jni/spec/jniTOC.html). This article focuses on calling methods in the direction of using Java from C.

Loading and attaching the JVM

JNI loads the JVM on a native app and runs Java code on it. You can create a JVM with JNI_CreateJavaVM ().

include <jni.h>

JNIEnv *env;
JavaVM *jvm;

 // JVM startup options, etc.
JavaVMInitArgs vm_args;
vm_args.version = JNI_VERSION_1_8;
JNI_GetDefaultJavaVMInitArgs(&vm_args);

 // load the JVM
JNI_CreateJavaVM(&jvm, (void **)&env, &vm_args);

I wondered if I should call JNI_DestroyJavaVM () when I finished using it, [Call API](https://docs.oracle.com/javase/jp/8/docs/technotes/guides/jni/spec/ invocation.html # unloading_the_vm) says "VM unloading is not supported."

Certainly, if you try to reload after unloading, you will get an error (JNI_EEXIST). That doesn't mean you need to route the jvm variable. Attach and use the JVM loaded in the same process.

 // Get the Java VM loaded in the past
JNI_GetCreatedJavaVMs(&jvm, 1, NULL);
 // Attach to get JNIEnv
(*jvm)->AttachCurrentThread(jvm, (void **)&env,NULL);

Instance creation and method call

For the method call, specify the method name and its signature (all argument and return types, details will be described later). Since Java is a language that can overload methods, the process to be called is uniquely determined by the set of argument types and method names.

First, get the method ID that indicates the process to be executed by GetMethodID () or GetStaticMethodID (). Only the constructor gets the special method name <init> with GetMethodID (). Others are specified according to the method definition. The following example is an example of executing the static method hello () of the self-made class Test.

 // First, find the applicable class
jclass clazz = (*env)->FindClass(env, "Test");
 // Get the method ID, static method with no arguments and return value void in this example
jmethodID id = (*env)->GetStaticMethodID(env, clazz, "hello", "()V");

Next, execute the following routine that takes the obtained method ID as an argument. <Type> contains a character string according to the return value such as Void or ʻObject. For each suffix with ʻA, pass the argument as an array, and for V, pass it as va_list. No mark takes a variadic argument.

If you call hello () earlier, it will look like this.

 // Call the method by passing the class, method and arguments obtained above.
(*env)->CallStaticVoidMethodA(env, clazz, id, NULL);

Handle Java variables in C

When calling a method, a Java object may be returned as an argument or return value. Each primitive type has a corresponding C type, and reference type variables (instances and arrays) are represented as references in the JVM with the type jobject. All of these types are members of the jvalue union. The table for each size etc. can be found in JNI Type and Data Structure. There is.

typedef union jvalue {
    jboolean z;
    jbyte    b;
    jchar    c;
    jshort   s;
    jint     i;
    jlong    j;
    jfloat   f;
    jdouble  d;
    jobject  l;
} jvalue;

Signature and how to check it

Signatures are a way to represent Java types. All patterns are shown in the table below. Arrays and methods are a combination of other types, and fully qualified classes specify the package delimiter as /, such as Ljava / lang / Object;.

Type Signature Java Type
Z boolean
B byte
C char
S short
I int
J long
F float
D double
Lfully-qualified-class; Class
[type array
(arg-types) ret-type method

You can see the actual signature with the -s option of the javap command. In the example below, the class file generated from jnigo / TestClass.java is read.

$ javap -s TestClass.class
Compiled from "TestClass.java"
public class TestClass {
  public boolean vboolean;
    descriptor: Z
  public byte vbyte;
    descriptor: B
...

  public static double smvdouble();
    descriptor: ()D

  public static TestClass smvclass();
    descriptor: ()LTestClass;

...

For example, the signature of TestClass isLTestClass;, so the function that takes TestClass as the return value with no arguments is () LTestClass;. In addition to specifying the class file directly, you can also specify FQCN and display it like javap -s java.lang.String.

Exceptions and error handles

On the native code side, the exception that is raised in JNI cannot be known unless it is actively checked. ʻExceptionCheck () can only get exceptions with or without exceptions, and ʻExceptionOccurred () can get exceptions. ʻExceptionDescribe ()` prints a stack trace to stderr.

If you use ʻExceptionOccurred ()`, you can write the equivalent of try ~ catch in C, but for panic debugging for the time being, it is easy to stop the process as follows.

if ((*env)->ExceptionCheck(env)) {
  (*env)->ExceptionDescribe(env);
  exit(1);
}

The error code of JNI itself is a failure unless it is JNI_OK. If the error requires resolution, you can handle it with the following error numbers.

/*
 * possible return values for JNI functions.
 */

define JNI_OK           0                 /* success */
define JNI_ERR          (-1)              /* unknown error */
define JNI_EDETACHED    (-2)              /* thread detached from the VM */
define JNI_EVERSION     (-3)              /* JNI version error */
define JNI_ENOMEM       (-4)              /* not enough memory */
define JNI_EEXIST       (-5)              /* VM already created */
define JNI_EINVAL       (-6)              /* invalid arguments */

Global reference and garbage collection

Even if the reference type variable is in use in C, it will be freed by garbage collection after the reference disappears on the Java side. By controlling the acquisition and release of global references on the native code side, its behavior can also be controlled. You can get a global reference with NewGlobalRef () and release it with DeleteGlobalRef ().

 // Get global reference, obj is a reference type variable
jobject ref = (*env)->NewGlobalRef(env, obj);
 // Release the created global reference
(*env)->DeleteGlobalRef(env, ref);

Afterword

I mainly explained how to call Java methods from C in JNI. The above information is sufficient for simple purposes, but if you want to use it in earnest, you will need to use JNI more, such as the behavior of garbage collection and consideration for performance. It looks like you can do something interesting with the JVM from native code.

reference

[About IBM Knowledge Center-Java Native Interface (JNI)](https://www.ibm.com/support/knowledgecenter/ja/SSYKE2_8.0.0/com.ibm.java.lnx.80.doc/diag/understanding/ jni.html) Best Practices for Using Java Native Interface juntaki/jnigo: JNI wrapper for Go

Recommended Posts

Call Java library from C with JNI
Call Java from JRuby
Call a method with a Kotlin callback block from Java
Call Kotlin's sealed class from Java
Code Java from Emacs with Eclim
Work with Google Sheets from Java
Call TensorFlow Java API from Scala
Call a C function from Swift
Encrypt with Java and decrypt with C #
Note: Differences from Java as seen from C #
Call GitHub API from Java Socket API part2
API integration from Java with Jersey Client
Call Java method from JavaScript executed in Java
Link Java and C ++ code with SWIG
Calling java from C ++ on Android NDK
Getting Started with Java Starting from 0 Part 1
Call Java methods from Nim using jnim
Try calling Nim from Java via JNI
Execute Java code from cpp with cocos2dx
[Android] Call Kotlin's default argument method from Java
Run Rust from Java with JNA (Java Native Access)
[Java] Set the time from the browser with jsoup
Java method call from RPG (method call in own class)
Text extraction in Java from PDF with pdfbox-2.0.8
Use Matplotlib from Java or Scala with Matplotlib4j
Use fast Mapping library MapStruct with Lombok and Java 11
How to call functions in bulk with Java reflection
Solving with Ruby, Perl and Java AtCoder ABC 128 C
[Java] How to encrypt with AES encryption with standard library
Java language from the perspective of Kotlin and C #
Use C library with Swift using Clang Modules (modulemap)
[Note] Create a java environment from scratch with docker
Call a program written in Swift from Processing (Java)
Generate models from JSON to Swift, PHP, C #, JAVA
Read temperature / humidity with Java from Raspberry Pi 3 & DHT11
Java coverage measurement library
Install java with Homebrew
Changes from Java 8 to Java 11
Sum from Java_1 to 100
Change seats with java
JAVA object mapping library
Install Java with Ansible
Eval Java source from Java
Comfortable download with JAVA
Access API.AI from Java
Switch java with direnv
From Java to Ruby !!
Java bidirectional map library
Download Java with Ansible
JAVA constructor call processing
Java CSV library "opencsv"
Let's scrape with Java! !!
Build Java with Wercker
Java tree structure library
Endian conversion with JAVA
Find the address class and address type from the IP address with Java
[Java] Get Charset with Apathce Tika / Initialize String from Charset [Kotlin]
Implement Java Interface in JRuby class and call it from Java
Kotlin post- and pre-increment and operator overload (comparison with C, Java, C ++)
[Kotlin] Get Java Constructor / Method from KFunction and call it
Investigated how to call services with Watson SDK for Java