UnsatisfiedLinkError when using a JNI native library from Grails application - java

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.

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.

Related

Loading native library with Spring Boot

I have a simple Spring Boot project which loads native libraries. The problem is that I have no idea how to specify the path to the native library when running the application.
I have read tons of posts explaining how to set java.library.path but every single one leads to
java.lang.UnsatisfiedLinkError: /path/to/lib/libconnector.so: libconnector.so: cannot open shared object file: No such file or directory
The project works if I run these two commands in a sequence from command line:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/path/to/lib
./gradlew bootRun
The library is loaded and works. But I am unable to specify the library path in my gradle file. I tried
run {
systemProperty 'java.library.path', file('/path/to/lib')
}
bootRun {
systemProperty 'java.library.path', file('/path/to/lib')
}
and all sorts of variations of this. Also tried adding VM arguments to my IDE etc. but nothing works. Could someone explain what am I doing wrong?
This is how I load the native library (located in $projectRoot/lib):
static {
// load connector library
File lib = new File("lib/" + System.mapLibraryName("connector"));
System.load(lib.getAbsolutePath());
}
I finally solved my problem. I should be passing LD_LIBRARY_PATH as an environment variable instead of java.library.path as system property when running the application.
The following Gradle modification solved my issue:
tasks.withType(JavaExec) {
environment('LD_LIBRARY_PATH', 'lib')
}
You can write a init method to auto-set java.library.path,
here are some codes:
String path = "/path/to/lib";
String path = System.getProperty("java.library.path");
// if os is windows
path += ";" + classPath.getCanonicalPath();
// if os is linux
path += ":" + classPath.getCanonicalPath();
System.setProperty("java.library.path", path);
Note: run this method first.
try to load the lib from your class ( just to test)
NB : i'am not sure but if you need to use so file you must be on a linux OS.
public class Test {
static {
try {
System.loadLibrary("yourSohere");
// or System.load("/path/to/lib.so");
}
}
}
You can simply use:
dependencies {
compile files('libs/something_local.jar')
}

jna Native.LoadLibrary does not manage to load library on server (working in local)

I use JNA to load a c++ library (.so) in a java project. I package my library inside the jar, and load it from the jar when instantiating the java class that uses it. I do all this like so:
mvn install compiles the c++ code and packages the outcome dynamic library inside the jar.
I call in a static context when instantiating the LibraryWrapperClass the following
System.load( temp.getAbsolutePath() );
where temp is a temporary file containing the library which was found in the jar. This code is based on the work found here adamheinrich
- I call Native.loadLibrary(LIBRARYPATH) to wrap the library into a java class.
private interface Wrapper extends Library {
Wrapper INSTANCE = Native.loadLibrary( C_LIBRARY_PATH, Wrapper.class );
Pointer Constructor();
...
}
I run tests and validate that the library was found and up and running.
I use a java web project that depends on this project. It uses tomcat and runs fine in local.
My issue is that when I deploy on the server, the LibraryWrapperClass cannot instantiate. Error on server is:
java.lang.NoClassDefFoundError: Could not initialize class pacakgeName.LibraryWrapperClass
at java.base/java.lang.Class.forName0(Native Method)
at java.base/java.lang.Class.forName(Class.java:375)
at org.hibernate.annotations.common.util.StandardClassLoaderDelegateImpl.classForName(StandardClassLoaderDelegateImpl.java:57)
at org.hibernate.boot.internal.MetadataBuilderImpl$MetadataBuildingOptionsImpl$4.classForName(MetadataBuilderImpl.java:758)
at org.hibernate.annotations.common.reflection.java.JavaReflectionManager.classForName(JavaReflectionManager.java:144)
at...
This error seems that the library is found, since there is not the UnsatisfiedLinkError exception thrown. But something else is failing. Do someone know what could happen? How could I debug?
I recall that everything works perfectly in local.
How could I debug?
1. with strace
strace will give you what files Tomcat is trying to open : strace -f -e trace=file -o log.txt bin/startup.sh
After this, look for packageName in log.txt, or other files not found with :
egrep ' open.*No such file' log.txt
2. with JConsole
Enable JMX, launch a JConsole, go to VM summary tab, and check/compare very carefully VM arguments/classpath/library path/boot class path
3. dependency listing with ldd
If a dependency issue is likely to be the problem, the ldd sharedLibraryFile.so command lists all the dependencies and allows to track which one might be missing.

Start Dropwizard with config.yaml from resources

I have a dropwizard question. I use Dropwizard with SBT (which works pretty fine).
If I run my application i package it with:
$ sbt clean assembly
And than run the application with:
$ java -jar APPLICATION.jar server
The problem is with this command Dropwizard doesnt load my config file (config.yaml), which is in the resources located.
Regarding the Dropwizard Docs I always have to give the config file as parameter like:
$ java -jar APPLICATION.jar server config.yaml
This works fine and it loads the application but is there any possibility to tell Dropwizard to load directly the config.yaml file, because my configuration in the config.yaml file is static and it is always the same. Settings like Database etc which are changing from Server Stage to Server Stage are made as Enviroment Variable which I load with EnvironmentVariableSubstitutor.
Thanks
Use class ResourceConfigurationSourceProvider:
#Override
public void initialize(final Bootstrap<ExampleConfiguration> bootstrap) {
bootstrap.setConfigurationSourceProvider(new ResourceConfigurationSourceProvider());
// The rest of initialize...
}
And then invoke the application like:
java -jar APPLICATION.jar server /resource-config.yaml
(note the initial /)
While this answer is very late, just thought I'd put this here. There is a dirty little hack to make it work so that you don't have to provide config.yaml in your application arguments.
Basically, you can submit a new String[] args to the run() method in the dropwizard application.
public class ApplicationServer extends Application<Config> {
public static void main(String[] args) {
String[] appArgs = new String[2];
appArgs[0] = args[0]; // This will be the usual server argument
appArgs[1] = "config.yaml";
new ApplicationServer().run(appArgs);
}
#Override
public void run(Config configuration, Environment environment) {
// Configure your resources and other application related things
}
}
I used this little trick to specify which config file I wanted to run with. So instead of specifying config.yaml, I would give my second argument as DEV/UAT/STAGE/PROD and pass on the appropriate config file to the run method.
Also interesting to have a look at:
earlye/dropwizard-multi-config
<dependency>
<groupId>com.thenewentity</groupId>
<artifactId>dropwizard-multi-config</artifactId>
<version>{version}</version>
</dependency>
It allows overriding and merging multiple config-files passed on the java command-line like:
java -jar sample.jar server -- sample.yaml override.yaml
Here you pass (1) sample.yaml as the primary configuration (e.g. having default values) and (2) override.yaml as the override. The effective config is a result from merging both in order of appearance: (1) defaults will be overwritten and merged with (2).

Error looking up function 'HelloWorld': The specified procedure could not be found

I am trying to access a VB .NET dll method that I have made in my java code.
I am trying to achieve this using JNA.
Here's my Java code.
Hello.java (invokes the dll method)
import com.sun.jna.Native;
public class Hello {
public static void main(String[] args) {
String myPath = System.getProperty("user.dir");
System.setProperty("java.library.path", myPath);
NativeInterface nInterface = (NativeInterface) Native.loadLibrary(
"SampleDLLProject", NativeInterface.class);
nInterface.HelloWorld();
}
}
And here's the interface
NativeInterface.java
import com.sun.jna.*;
public interface NativeInterface extends com.sun.jna.Library {
public void HelloWorld();
}
The error that I am facing is:
Exception in thread "main" java.lang.UnsatisfiedLinkError: Error looking up function 'HelloWorld': The specified procedure could not be found.
at com.sun.jna.Function.(Function.java:179)
at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:345)
at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:325)
at com.sun.jna.Library$Handler.invoke(Library.java:203)
at $Proxy0.HelloWorld(Unknown Source)
at Hello.main(Hello.java:10)
Also I checked the dll for any kind of discrepancy using ildasm.
There is no difference between the method name present in dll and name in Hello.java.
Interestingly, I tried to test the code for the puts() method of windows dll msvcrt.dll and it works perfectly fine. So the problem is for the DOT NET dlls .
Please help!
The library you are using is for loading methods from native libraries (such a windows.dll and msvcrt.dll), these libraries are compiled into native code that can run directly on x86 or x64 processors.
Libraries created in VB.NET are not native libraries, they are CLI (Common Language Infrastructure) Assemblies that are compiled into CIL (Common Intermediate Language) which is then compiled on the fly to run any kind of processor (just like Java!)

How to use a JNI native library from Grails application

I'm developing a Grails web application and I need to use a JNI native library to access some specific hardware. For a simple Java application (see below) it works fine. To do this I just have to add the JAR to the Java build path and specify the "Native library location" (I use SpringSource Tool Suite on Windows7).
Working example:
import conceptacid.nativedriver.Driver;
public class Main {
public static void main(String[] args) {
System.out.println("starting...");
System.loadLibrary("AudioCardDriver");
Driver driver = Driver.instance();
String driverDescription = driver.getDriverDescription();
System.out.println( "Native application driver: " + driverDescription);
}
}
However, when I try to add it into my Grails application it fails:
Bootstrap.groovy:
import conceptacid.nativedriver.Driver;
class BootStrap {
def init = { servletContext ->
System.loadLibrary("AudioCardDriver");
Driver driver = Driver.instance();
String driverDescription = driver.getDriverDescription();
System.out.println( "Native application driver: " + driverDescription);
}
def destroy = {
}
}
the first line System.loadLibrary("AudioCardDriver"); is executed silently without any exception but the next line where I try to use my native code Driver driver = Driver.instance(); fails:
Running script C:\grails\scripts\RunApp.groovy
Environment set to development
[groovyc] Compiling 1 source file to D:\Projects3\mbr\target\classes
[delete] Deleting directory C:\Users\VShmyrev\.grails\1.3.7\projects\mbr\tomcat
Running Grails application..
2012-02-24 15:19:49,690 [main] ERROR context.GrailsContextLoader - Error executing bootstraps: java.lang.UnsatisfiedLinkError: conceptacid.nativedriver.AudioCardDriverJNI.swig_module_init()V
org.codehaus.groovy.runtime.InvokerInvocationException: java.lang.UnsatisfiedLinkError: conceptacid.nativedriver.AudioCardDriverJNI.swig_module_init()V
at grails.util.Environment.evaluateEnvironmentSpecificBlock(Environment.java:251)
...
Caused by: java.lang.UnsatisfiedLinkError: conceptacid.nativedriver.AudioCardDriverJNI.swig_module_init()V
at conceptacid.nativedriver.AudioCardDriverJNI.swig_module_init(Native Method)
at conceptacid.nativedriver.AudioCardDriverJNI.<clinit>(AudioCardDriverJNI.java:70)
at conceptacid.nativedriver.Driver.instance(Driver.java:35)
at conceptacid.nativedriver.Driver$instance.call(Unknown Source)
at BootStrap$_closure1.doCall(BootStrap.groovy:7)
... 26 more
Application context shutting down...
I'm sure I put the DLL into a directory which is in my system PATH but it doesn't help.
What is the right way to use a native library in Grails application both in a development environment and in production?
Your DLL needs to be on a path specified in the Java system property java.library.path. On Windows the PATH environment variable and Linux the LD_LIBRARY_PATH environment variable are added to this system property. You can try logging the java.library.path system property to see if Java is looking in the right place for your DLL.
I'm guessing that the native library is being loaded multiple times in different classloaders, so forking a new JVM when running the app might help.

Categories