Thursday, September 18, 2008

Going Native

I have a new project (and a new work) which involves starting and interacting with a python interpreter from the JVM.

I did my requisite Google search. At first, I thought I would do this using an existing library, JPype, but this works the other way around - the JVM is embedded in Python. So I moved to the next option, JNI. JNI is a pain in the ass, so after spending half a day trying to compile the generated stubs - not even talking to the Python C library, mind you - I decided to go back to Google.

I thought that, perhaps, someone had written a way to generate JNI headers and native methods. There are a few options out there, actually, but the one I settled on is JNA. It's open source, cross-platform[1], and surprisingly easy to use. An added benefit is that Timothy Wall, a major project contributor, is pretty responsive on the user mailing list.

It's simple to use:


package com.sun.jna.examples;

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;

/** Simple example of native library declaration and usage. */
public class HelloWorld {

public interface CLibrary extends Library {
CLibrary INSTANCE = (CLibrary)
Native.loadLibrary(
(Platform.isWindows() ? "msvcrt" : "c"),
CLibrary.class);

void printf(String format, Object... args);
}

public static void main(String[] args) {
CLibrary.INSTANCE.printf("Hello, World\n");
for (int i=0;i < args.length;i++) {
CLibrary.INSTANCE.printf(
"Argument %d: %s\n", i, args[i]);
}
}
}


That's it. You now have native access to printf. All of the native calls are handled through a proxy generated at run time. You don't have to write all the methods in the library, either. The resolution is done on the fly. If you need to add another native call, simply add it to the interface.

The library begins to get a little hairy when dealing with pointers and references. For the most part, pointers are handled for you (it's Java, every things a pointer). But what about primitives, you ask? JNA solves this through a nice set of Pointer and PointerType classes.

So now if you have a C function like the following:


void setSomeValue(int* retValue)


you can map it via the following:

public interface MyNativeLibrary extends Library {
...

public void setSomeValue(IntByRefrence retValue);

...
}


In Java, we can now call the method like so:

IntByReference retValue = new IntByReference();
MyNativeLibrary.INSTANCE.setSomeValue(retValue);
System.out.println("retValue = " + retValue.getValue());


There is also a PointerByReference, which can be used to do the same thing with pointer-based arrays, or String values.

Structures and callbacks are also very simple to map. I'll take a look at these in a followup post.

I don't know why anyone would write JNI anymore[2]. This library has made access to native code amazingly simple, and surprisingly intuitive. I would would highly recommend it to anyone who is avoiding a solution, simply because they don't want to make native calls.


[1] Linux, Mac OS X, and Windows, for now.

[2] Ok, there is some overhead, but through clever caching of the function pointers, it's pretty fast. I'll have to find some numbers on it.

0 comments: