aliquote.org

Embedding C code in a Java app

July 13, 2008

Suppose your employer ask you to develop a small application that has to be deployed to a wide range of researchers in the near future. Obviously, you’re working in a Micro$oft environment (Yes, what else?) and you don’t want to spend your time reading MS SDK, looking for a decent 32 bits compiler with a minimalist but useable EDI. Further, as you probably have other things to do during your daily job, this job will probably become your homework and, of course, as you’re a well educated people, you work at home on a Mac or a Linux workstation. It’s time to learn how to wrap C and Java together in a small cross-platform application.

There is a Sun tutorial, and other can be found on the internet. We will focus on a very primitive example, namely the computation of the first decimals of Pi with the Machin’s formula which is given by the relation

$$ \frac{\pi}{4}=4\text{arctg}\left(\frac{1}{5}\right)-\text{arctg}\left(\frac{1}{239}\right) $$

For those who are interested in π computation, I recommend the following book: J.-P. Delahaye, Le fascinant nombre Pi (Pour la Science, 1997), but you could start with the Wikipedia article: Computing Pi. There is also a Javascript online calculator.

Starting with Java

For now, we shall begin with an even more simplistic example: The famous Hello World! The code in Hello.java might look a bit strange at first sight, but it only contains a single class with static main method, and the method sayHello() has been declared as native:

class Hello {
  public native void sayHello(); 
    static {
      System.loadLibrary("hello");
    }

  public static void main(String[] args) {
    Hello h = new Hello();
    h.sayHello ();
  }
}

Next, all we have to do is to call javah which will generate the C headers for us. In fact, the *.h file includes the C declarations for the methods that were declared native in our file *.java:

$ javah -jni Hello
$ ls
Hello.class	Hello.h		Hello.java

The C side

We now have three files and we can create a C file with the following code:

#include <jni.h>
#include <stdio.h>
#include "Hello.h"
JNIEXPORT void JNICALL Java_Hello_sayHello (JNIEnv *env, jobject obj) 
{
  printf("Hello world!\n");
  return;
}  

It can be seen that we are passing the same parameters to the method as in the *.h file, but now with names specified. Now, we have to compile our (dynamic) library; thus, we don’t link the program, and just compile it with the appropriate libraries. Depending on your platform, the java SDK might be installed in /usr/share/ or /opt/ (Linux users) or /System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Headers/ (Mac users):

$ gcc -c -I/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Headers/ \
  -o hello.o Hello.c

If all went file, you should get the file hello.o.

Assembling the different pieces of code

Now, we just have to create a definition file, ending with *.def. It is used to specify the name of the functions that are to be exported, or in other words the visible functions. It looks like this:

EXPORTS Java_Hello_sayHello

Note that it is not a mandatory file for our example. To produce the library, we just call gcc with the -shared argument. This is for Windows and Linux users:

$ gcc -shared -o hello.dll hello.o hello.def

For Mac users, however, we must adapt this linking phase because the -shared option is not supported on Mac OS X. All is explained in Core Java APIs on Mac OS X. Instead, we have to use the -dynamiclib flag. If we look at the gcc documentation, e.g. man gcc, we can check that:

-dynamiclib
When passed this option, GCC will produce a dynamic library 
instead of an executable when linking, using the Darwin 
libtool command.

Thus, here is how to end up correctly:

$ gcc -dynamiclib -o libhello.jnilib hello.o -framework JavaVM

This should create the file libhello.jnilib. Then, we can test our example by issuing:

$ java Hello
Hello world!

The complete source code is available here: 032_JNI.zip.

Sidenote

Here are two technical notes from Apple Dev Center that can be useful for the Java Mac beginner: