Monday, 21 May 2012

An Affair with the Java Native Access (JNA)

I was recently speaking to a colleague about my first couple projects here at NAG. The first project was learning to call the Library from Python using c-types (thanks to Mike Croucher’s blog which helped immensely). Next, was a project using the Java Native Interface (JNI), which I had difficulty using. After hearing the above two pieces of information, my colleague recommended I look into Java Native Access (JNA) as it was very similar to c-types in Python. Thus began a brief love affair! I say ‘love affair’ because my experience the JNA was a bit of a roller coaster of highs and lows. In the beginning, the JNA and I got along great. As time went on, I was left sitting at the computer screen wondering what to do next, hoping for the JNA to fix things.

Background of JNI

NAG already has a thorough technical report on our website for calling the NAG Library using the Java Native Interface. This includes creating header files, compiling java files, compiling the interface library, and running the program. Seems like lots of work, even for simple functions. I was hoping the JNA would be easier.

First date with the JNA

To start using the JNA you just need to go to download the .jar file from https://github.com/twall/jna. Download the file and then move it to the directory you will be working in. Unzip it to create a com folder and you’re done! You can now start using it. Whenever you need to use a package from the JNA, just import it at the top of your Java file.

My first impression of the JNA started off on a high note. I found it extremely easy to start using the interface. Looking at code for calling the Bessel function (routine s17acc):


import com.sun.jna.Library;

import com.sun.jna.Native;

import com.sun.jna.Platform;

public class HelloWorldBessel { public interface CLibrary extends Library { CLibrary INSTANCE = (CLibrary) Native.loadLibrary("/opt/NAG/cll3a09dgl/lib/libnagc_nag.so", CLibrary.class);

double s17acc(double k,int fail); } public static void main(String[] args) {

int k=0; int retCode=0;

System.out.println("Y0(" + k + ") is " + CLibrary.INSTANCE.s17acc(k,retCode));

}

}

Using the JNA takes only a couple lines! Feel free to compile and run it yourself! No header files, no Javac/Javah code to compile, no JNI in sight! I was excited how easy the JNA was and very eager to try other functions.

Falling in love again

The second impression of the JNA was very nice as well. The reason I ‘fell in love’ was that it took only a couple more lines of code from the above Bessel example to call the linear equation solver (f04arc). Once variables are initialized and we’ve declared the f04arc method, all we need is a call to

CLibrary.INSTANCE.f04arc(n, a, tda, b, x,fail);

Again, it was nice and easy to use. Note: if the NAG function detects an error in the inputs, the error is automatically printed, and the entire program is terminated. A small inconvenience, but all relationships have some skeletons hidden in the closet.

Never getting Calledback

The JNA and I hit of rough patch with the callbacks. It proved to be difficult on getting the right calls, in addition to mapping C structures to Java. It took me pages of online forums and reading through user comments, but you end up needing to add extra lines when loading the library, creating the callback, and adding a structure class. A brief excerpt:

public interface CLibrary extends Library { CLibrary INSTANCE = (CLibrary)
Native.loadLibrary("/opt/NAG/cll3a09dgl/lib/libnagc_nag.so", CLibrary.
class);
interface acallback extends Callback{
double invoke(double x);
}
void d01ajc(Callback fun, double a, double b, double epsabs, double epsrel, int max_num_subint, DoubleByReference result, DoubleByReference abserr, Nag_QuadProgress qp,int fail);
}

CLibrary.
acallback fn=new CLibrary.acallback(){
public double invoke(double x){
return(x*x*x);
}
};

You’ll note when calling d01ajc, I needed to pass a Quad_Progress structure into the function. To do this, we need to create a structure with variables, but if you examine the contents of these variable after the call d01ajc, their information is meaningless (comments on how the call to d01ajc populates these fields are welcome below!). On top of this, the JNA does not handle errors very well. Since it is not apart of Java itself, I would oftentimes get “An error occurred outside Java” and left wondering which argument I got wrong.

Timings

Averaged over 100 runs, the timings are:

JNA JNI
s17acc .0015s .0010s
f04arc .0082s .0015s
d01ajc .0377s .0388s

Back to the JNI

My first impression of the JNA was nice. It was simple and no 'glue code' was required. Upon further investigation I found a couple of drawbacks which include:

  • Mapping C structures to Java
  • Slightly slower than JNI
  • Error Handling

Since we already have most (if not all) the wrappers for the Library using the JNI, I will stick with that for now. Alas, my time with the JNA was fun while it lasted.

5 comments:

  1. Whilst a NAG routine can be called so that it will automatically print an message and terminate if an error is detected, this behaviour can be changed if required.

    The default behaviour for the NAG C library is a quiet exit (i.e. no message is printed and the calling program is not terminated) as long as a non-null value is supplied for the fail argument.

    ReplyDelete
  2. JNA can usually translate native crashes into a Java exception by setting jna.protected=true. This is not recommended for production systems, since native crashes are not generally recoverable, and full support for the translation often requires additional environment configuration to work properly.

    ReplyDelete
    Replies
    1. Hi - I am having a problem with callbacks and my callback in java is never getting called. I have something similar to what you have above. Any pointers about what could be wrong?
      Kas

      Delete
    2. Hi Kas,
      Off the top of my head, I'm not sure. Can you provide any more details to what's going wrong?
      Feel free to email me [spector(at)nag(dot)com] and I can pass along the code I used to generate the above post.
      Brian

      Delete
  3. Hi Brian, I just did a completely automated interface for Sulum in JNA, that was a pleasant experience compared to recompiling JNI code. I like this approach much better, since it's very close to other marshalling systems like .NET and C Python. Since they are more alike you can easier reuse code and auto generate API's.

    ReplyDelete

NAG moderates all replies and reserves the right to not publish posts that are deemed inappropriate.