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.
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.