Tutorial: Use C code in your Reactive Block application with JNI

Summary

These tutorials explains how to use foreign (as in non-Java) code in your Reactive Block application. Another term for foreign is native, because embedding non-Java code usually introduces platform and portability issues.

Make sure you have read the intro foreign-code-intro to prepare what you need for this tutorial.


Doing the tutorial

Alt text

Download the tutorial named “(Level 3) Foreign code v4: JNI”

Contrary to the JNA and BridJ tutorials where we take C/C++ and generate Java code from it, with JNI we work the opposite way by converting a Java file to a C header file.

Part 1 - Make Java class for for shared library

  • Look at the project in the Package Explorer view. You will see the src directory. Inside there you will WatchdogInterface.java. Note package definition, you will need it later.

  • You should also look at wd-jni.h inside the solution directory. That is the file that contains the C library we are going to interface.

Alt text

Part 2 - Understand functions of shared library

The functions in Java that we are going to make in C are:

  • public native static void setWdDeviceName(String deviceName);: Set filename of watchdog device.
  • public native static String getWdErrorMessage();: Gets most recent error message. The message is allocated in C heap and copied into a Java String when returned.
  • public native static void clearWdErrorMessage();: Clear last error message and release memory.
  • public native static int enableWd(int timeout);: Enable watchdog, parameter is timeout value.
  • public native static int getWdRemainingTime();: Returns remaining time until watchdog fires, this is similar to the stupid countdown timer on all atomic bombs in old James Bond movies.
  • public native static int getWdTimeout();: Return current timeout value.
  • public native static int disableWd();: Disable watchdog
  • public native static int heartbeatWd();: Send heartbeat to reset watchdog counter.
  • public native static void closeWd();: Close files and release memory.

All int return values < 0 indicates error.

Note that all the functions have a C counterpart in wd-jni.h.

Part 3 - Understand reactive block and functions

Alt text

The block is shown in the image above and with exception of the init and stop pin, all functions the same as in the Java library. In addition we have added a state check to query if watchdog is enabled.

We will take a closer look at some functions:

init and stop functions:

Alt text

  • The C interface is loaded in init() method.
  • We are using Java String as parameters and return values, but must convert them in the C-code later.
  • The stop() method closes file and releases memory in the C-code.

Part 4 - Generate C header file and C-code for library on Raspberry-Pi

Alt text

Execute the following steps to generate the C header file we will use for to make the C-code

  • mkdir wd-jni
  • cd wd-jni
  • mkdir -p com/bitreactive/tutorial/foreigncode/jni/wdjni/
  • Transfer WatchdogInterface.java from your computer to to wd-jni directory
  • mv WatchdogInterface.java com/bitreactive/tutorial/foreigncode/jni/wdjni/
  • javah com.bitreactive.tutorial.foreigncode.jni.wdjni.WatchdogInterface

Now comes the hard part with JNI, we need to make the C-Code that can be compiled and linked to become the wanted shared library.

Different from BridJ and JNA all memory allocation and deallocation takes places on the (native) C side. Each call will have two parameters in addition the the parameters defined in the function itself, see the example below where the functions setWdDeviceName() and getWdErrorMessage() are highlighted both in Java and in the C header file.

Alt text

  1. JNIenv *env: This is interface to the Java VM. It includes all of the functions necessary to interact with the JVM and to work with Java objects.
  2. jclass *class: This is a reference to the object where the native method has been declared.

Implementatation of setWdDeviceName():

Alt text

  1. Contents from the Java String object is copied in C type nul-terminated string. Java Strings are UTF-16 and are converted to UTF-8. ASCII is a subset of UTF-8, so as long you as you do not use any exotic characters you do not need to worry about that.
  2. Does the function (sets name of watchdog file)
  3. Releases memory allocated in 1.

Implementatation of getWdErrorMessage():

Alt text

  1. A local method is called to get error message.
  2. The message is converted from C type nul-terminated string to Java String. The Java String object will be allocated in Java heap.
  3. The reference to the String is returned.

You need to implement all all functions in the generated C header file.

More about JNI here.

Part 5 - Build shared library on Raspberry-Pi

A ready made solution is available in the folder solution in the project you downloaded earlier.

Make sure to not overwrite your own work in com_bitreactive_tutorial_foreigncode_jni_wdjni_WatchdogInterface.c.

See Part 1 for download and transfer instructions.

Once you have downloaded these three or four files you can generate the shared library with these two commands:

  • chmod +x buildall.sh
  • ./buildall.sh

Alt text

The header file is generated with javah and gcc is used to compile and link the shared library. ./buildall.sh is a text file (script) that can be edited. You should look at the contents.

Part 6 - Build application and execute on Raspberry-Pi

  • Select Test WD JNI application, press Build from rightclick menu and build Java project. Select Java SE as project type.
  • Export the generated project (located in com.bitreactive.tutorial.foreigncode.jna.testwdjni_exe) as Runnable JAR and transfer to your Raspberry-Pi to the same directory as the shared library you generated in part 5.

Alt text

  • Try it (Hint1: Note you have to use sudo because of file rights) (Hint2: You may need to set java.library.path)

Alt text