Tomdee

19Feb/120

JNI and Gluegen

I know I can develop software faster using Java so I want to learn how to interface with native code using JNI. No solution seems particularly elegant and going for a completely manual approach seems to involve a lot of boiler plate and work.

I had a quick look around google and Wikipedia and a couple of options to help with code generation - SWIG and Gluegen. The latter particularly caught my eye, in particular the ability to have structs treated as Java classes.

It took me a little while to get a hello world app running, so instructions are below to remind me in future and for anyone else that might find it useful.

Obtaining and building Gluegen

  • git clone git://github.com/mbien/gluegen.git gluegen
  • cd gluegen/make
  • ant clean all

This gives the required jars in the build directory

  • gluegen.jar for generating the Java version of C code and the bridging C code.
  • antlr.jar for parsing C code.
  • gluegen-rt.jar required at runtime.

Creating a sample application

The C code

int one_plus(int a) {
 return 1 + a;
}

Taken from the Gluegen website

The Java code

import testfunction.*;
 class Test
 {
 static {
 System.loadLibrary("nativelib");
 }
public static void main(String args[])
 {
 System.out.println(TestFunction.one_plus(5));
 }
}

I'm jumping ahead a little here. This code assumes that the C code is in a package call testfunction and that there is a native lib called nativelib.

Using Gluegen

Running Gluegen creates binding C code and the Java code that defines the C methods.

Gluegen needs some configuration to guide its behaviour. It's here that the Java package and class names are defined.

Package testfunction
 Style AllStatic
 JavaClass TestFunction
 JavaOutputDir gensrc/java
 NativeOutputDir gensrc/native

To run Gluegen, I went for the ant approach. My build file is below

<?xml version="1.0"?>
 <project name="sampleProject" basedir=".">
 <path id="gluegen.classpath">
 <pathelement location="gluegen.jar" />
 <pathelement location="antlr.jar" />
 </path>
<taskdef name="gluegen"
 classname="com.jogamp.gluegen.ant.GlueGenTask"
 classpathref="gluegen.classpath" />
 <target name="build">
 <gluegen src="function.h"
 config="function.cfg"
 emitter="com.jogamp.gluegen.JavaEmitter">
 <classpath refid="gluegen.classpath" />
 </gluegen>
 </target>
 </project>

running ant build results in a new directory "gensrc" containing a "native" and a "java" directory.

The generated native code is the Java to C glue code. The only thing worth noting about it is that it pulls in JNI.h C header file.

The generated java code has the "native" method and pulls in the runtime lib.

package testfunction;
import com.jogamp.gluegen.runtime.*;
 import com.jogamp.common.os.*;
 import com.jogamp.common.nio.*;
 import java.nio.*;
public class TestFunction {
 /** Interface to C language function: <br> <code> int one_plus(int a); </code> */
 public static native int one_plus(int a);
 } // end of class TestFunction

 Building the results

For me, this was the most challenging part. Most of the above is covered quite well on the Gluegen website. The following isn't.

Building the C code

 gcc -Wl,-soname,libnative.so -o libnativelib.so -fPIC --shared gensrc/native/TestFunction_JNI.c function.c -I/usr/lib/jvm/java-6-sun/include -I/usr/lib/jvm/java-6-sun/include/linux /usr/lib/jvm/java-6-sun-1.6.0.26/jre/lib/amd64/server/libjvm.so -lc

Quite a lot here.

  • gcc is used to build the code
  • -Wl passes options to the linker. In this case, the shared object name is set to libnative
  • -o specifies the output filename
  • -fPIC was required to avoid an error message along the lines of
    • /usr/bin/ld: /tmp/ccI3vLJd.o: relocation R_X86_64_PC32 against symbol `one_plus' can not be used when making a shared object; recompile with -fPIC
  • --shared causes a shared object to be built
  • TestFunction_JNI.c is the Gluegen generated code and function.c is the actual implemenation. These are the two files that are actually being built.
  • the two -I options specify the locations of the JNI headers (jni.h)
  • libjvm.so is also to be passed in.
This results in a file libnativelib.so in the current directory.

Building the Java Code

javac Test.java gensrc/java/testfunction/TestFunction.java -cp ../gluegen/build/gluegen-rt.jar

More straightforward. The Gluegen code and the code I wrote to drive it is compiled. The gluegen runtime jar needs to be on the classpath.

Running the Result

java -Djava.library.path=. -cp gensrc/java:. Test
6

Running the Java requires the native lib path to be specified. The program then specifies 6 as expected. Phew!






Comments (0) Trackbacks (0)

No comments yet.


Leave a comment

No trackbacks yet.