Question

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.

Was it helpful?

Solution

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.

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top