JNI, JNA and JNR - Java access to native libraries

by Karl Ostendorf on Mon 05 Nov 2012 in java


Last week I tried to find a Java library to let me access native functions to let me change the current user running a program. The native function, setuid, is part of the standard C library on Linux and many other *nixes as well as OS X. It allows a program to be started as root, perform a privileged function such as opening a port below 1024 and then by "dropping privileges" to continue operation as a normal user.

I didn't find just one library to do this, I found three, each using a different method to access native library functions. The three methods, JNI, JNA and a new community development JNR, each have their own strengths so none are redundant.

The JNI library, jetty-setuid, from the Jetty project, neatly mavenized, compiled straightaway on OS X after a few tweaks to the POM to correct updated module names. However, it didn't build properly on Linux and after an hour of making adjustments to the POM I decided to throw in the towel. The JNI library compiled just fine from the command line, but either the Maven native plugin or its settings in the POM were broken. Additionally, I would prefer a JNA solution so that don't have to install an extra native library on my production systems.

In any case, isn't JNI overkill for a single call to setuid? Yes, JNI is fast but it's not as if we're calling native functions in a loop for CPU-intensive computations. So thankfully the jna-posix project offers a JNA library to do just the same thing. The problem with this library is however that it is no longer being actively maintained. Part of the JRuby project, its developers have moved on to develop, jnr-posix a new version of the same code based on a new technology called JNR. JNR, unveiled by @headius last year at OSCON, aims to be faster than JNA yet easier to program than JNI. My problem with JNR is the long list of dependencies it will add to a project: 5 JNR jars and 5 jars from the ASM project.

Faced with the dilemma of using JNI, and being forced to install a native library on my production systems; JNA, but using a project no longer maintained; or JNR, and adding numerous dependencies to my project that I don't need or want - I did the sensible thing and rolled my own JNA solution. See below, it's very easy:

import com.sun.jna.Native;

public class LibC {

    static {
        Native.register("c");
    }

    public static native int getuid();
    public static native int setuid(int uid);

    public static native int getgid();
    public static native int setgid(int gid);

    public static native Passwd getpwnam(String login);

    public static native int umask(int mask);

    private LibC() {
    }

}

My only dependency besides my two classes is JNA, (a single JAR) available via Maven and under active development. Tested on OS X and Linux (Debian Squeeze) it works flawlessly.