4

I have an application where I need to use a Native Library: libfoo.so

My code is as follows:

Accessor.java:

public class Accessor {        
    static {
        String path = "/usr/lib/libfoo.so";
        System.load(path);
    }
    ...
}

This works perfectly fine when I deploy my war file in a standalone tomcat server.

The problem is when I try to run the embedded tomcat server when you run:

grails run-app

I get an UnsatisfiedLinkError:

Caused by UnsatisfiedLinkError: com.foo.bar.GFS_MALJNI.new_Accessor__SWIG_0(Ljava/lang/String;I)J
->>   39 | <init>    in com.foo.bar.Accessor 

Interestingly enough, if I change my BuildConfig.groovy file to fork mode, it also works.

BuildConfig.groovy:

grails.project.fork = [
   run: [maxMemory:1024, minMemory:64, debug:false, maxPerm:256]
]

I do not want to run it in fork mode.

Sohel Katchi
  • 191
  • 11
  • Possible equivalence to [this](http://stackoverflow.com/questions/9432460/how-to-use-a-jni-native-library-from-grails-application) question. – dmahapatro May 22 '13 at 15:09
  • It's not equivalent to that question, as he can't get it working at all. I can get it working in a standlone tomcat instance, but not on the embedded tomcat server (using grail run-app). He's also dependant on the library path, where I'm not since I'm hard-coding the path of the library. – Sohel Katchi May 22 '13 at 15:34
  • It's probably a classpath issue that's resolved with the fork mode, since this is one of benefits of the mode: "Isolation of the build classpath from the runtime classpath" –  May 22 '13 at 16:23
  • Can you eloborate? Why does the classpath matter when I'm specifying the absolute path of my native library? – Sohel Katchi May 22 '13 at 17:51

2 Answers2

3

I noticed that two different class loaders are being used.

In the non-forked mode, this class loader was being used: java.net.URLClassLoader

In the forked mode, this class loader was being used: groovy.lang.GroovyClassLoader

The native library works correctly in the forked mode, so I needed to come up with a hack to load the library with the GroovyClassLoader in the non-forked mode.

This is how System.load is defined in the JDK Source:

System.java:

public final class System {
    ...
    public static void load(String filename) {
        Runtime.getRuntime().load0(getCallerClass(), filename);
    }
    ...
}

It's calling load0 with the classloader and filename. The obvious solution is to call load0 with your own classloader, but you can't call it since it is package-protected.

When you write code in groovy, you have access to packge-protected and private methods/variables.

I can specify my own classloader and load the library, as such:

class Accessor {        
    static {
        String path = "/usr/lib/libfoo.so"
        //System.load(path);
        Runtime.getRuntime().load0(groovy.lang.GroovyClassLoader.class, path)
    }
    ...
}

I just tried it, and it's working in non-forked mode.

Sohel Katchi
  • 191
  • 11
0

My guess is that the Accessor class is being loaded multiple times in different classloaders within the same JVM (assuming grails runs in the same JVM as the embedded Tomcat). Test this by adding debug statements to the static block.

Jukka
  • 4,583
  • 18
  • 14
  • Interesting. In Non-Forked mode, where it is not working, the classLoader is: java.net.URLClassLoader@19845f72 In Forked mode, where it is working, the classLoader is: groovy.lang.GroovyClassLoader@cf47ba0 So now that I know this, what can I do about it? – Sohel Katchi May 22 '13 at 20:04
  • AFAIK, not much but just use the forked mode. The native library cannot be loaded in another classloader until the classloader that loaded it previously has been gc'd. – Jukka May 22 '13 at 21:15
  • I'm having other issues with forked mode -- mainly around logging. I posted another question on that [here](http://stackoverflow.com/questions/16696396/grails-logging-not-working-in-forked-mode) If I get that figured out, then I have no problem using forked mode. Although, I was able to resolve this JNI issue in non-forked mode, so it's not terribly important anymore. – Sohel Katchi May 23 '13 at 14:42