Sunday, 25 December 2011

Loading native libraries in Java

This is an old problem, so I thought I'd write down my current experiences to save others, and myself, pain in future.

You write a native library libFoo.so or foo.dll or libFoo.dylib etc. for Java. And you store it in a convenient location, not a system location, because your software shouldn't meddle with that. To use it you need to call System.loadLibrary("foo");. This will probably give you: "Exception in thread "main" java.lang.UnsatisfiedLinkError: no foo in java.library.path". Where did I go wrong?

System.loadLibrary looks in the Java library path, "java.library.path". Cool, let's set that in the program, just before we call loadLibrary. We can get some platform-independence by passing in the library path on the commandline:

String old = System.getProperty("java.library.path");
System.setProperty("java.library.path",old+":/usr/local/lib");

It doesn't find the library because you can't change "java.library.path" after starting the JVM. It just ignores your additional directory.

Everyone says set the environment variable LD_LIBRARY_PATH to (in my case) /usr/local/lib. This doesn't work either. On Linux and OSX at least Java ignores that variable when setting up java.library.path. In any case setting LD_LIBRARY_PATH globally for your application will screw up something else on your system. Not cool.

Third attempt. Set java.library.path on the java commandline:

-Djava.library.path=/usr/local/lib

Now you've changed the JVM so things could go wrong. Instead of having the system default library path where everything is, you've redefined it to a custom location. Unfortunately there's no universal way to ADD /usr/local/lib to java.library.path. So the best you can do is find the java library path on your system (by writing a Java program that outputs System.getProperty("java.library.path")) and then add /usr/local/lib to that value and finally specify the entire string to java:

-Djava.library.path=.:/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:/usr/local/lib

This is what I have to do on Mac OSX. Of course it's entirely platform-specific, which is stupid for a programming language that is supposed to be platform-independent. On the other hand this yucky solution is the best one on offer. Since when you've finished developing you'll be running it time and time again on the same platform it probably doesn't matter much.

Alternatively, you could just put your library in the current directory, which will work on Windows (reportedly) and Mac OSX but not Linux.

2 comments:

  1. Thanks for the post and great info, I am now struggling with the same problem,

    Finally, installation team were able to put native libraries into a folder which is already in library path. Problem resolved!

    Next problem: when to load the native library: when the application starts or when it is needed! does it make any difference? especially trying to load same library again and again?

    Seems simple, but not: when JVM starts: no one is actually watching logs, so when native library is loaded during startup: if an error happens: no one will notice it,

    If it is loaded when it is needed: then we will be able to display a message somewhere, i.e. UI, or soap message,

    and it is going to be accessed from different places, too,

    Seems second option is better: with a wrapper, maybe

    Thanks for the post again,

    ReplyDelete
  2. If it helps, I just load it in a static block in the class that uses it. So it loads automatically as soon as I access that class. Once loaded it should be available. No need to reload it. If you want to send a message back to the user you'll have to handle that separately:
    class bar
    {
    static
    {
    try
    {
    System.loadLibrary("foo");
    }
    catch ( Exception e )
    {
    System.out.println(e.getMessage());
    }
    }
    }

    ReplyDelete