Search code examples
javagradlejava-native-interface

Add SmartID Reader JNI library into existing gradle project


I'm trying to use Smart IDReader SDK trial version (https://habr.com/en/company/smartengines/blog/332670/) in my existing project with Gradle (project assembled into war-archive). The app workstation is centOS and I needn't support all architectures for native libs. So, I have libjniSmartIdEngine.so native lib and jniSmartIdEngineJar.jar. Also in SDK c++ *.h files and *java SWIG classes presented. What I'm try:

  1. add jar and so libs in /resources/.
  2. add compile files('lib/jniSmartIdEngineJar.jar', 'libjniSmartIdEngine.so') in build.gradle dependencies.
  3. load resource in static block inside my class where I want to use provided example code.

    static { URL jniWrapper = SmartIDReader.class.getResource("/res/jniSmartIdEngineJar.jar"); System.load(jniWrapper.getPath()); }

Jar and So placed in the same folder and I have error:

It's highly recommended that you fix the library with 'execstack -c <libfile>', or link it with '-z noexecstack'.Exception in thread "main" java.lang.UnsatisfiedLinkError: /jniSmartIdEngineJar.jar: /jniSmartIdEngineJar.jar: invalid ELF header (Possible cause: endianness mismatch)
at java.lang.ClassLoader$NativeLibrary.load(Native Method)
at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1941)
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1824)
at java.lang.Runtime.load0(Runtime.java:809)
at java.lang.System.load(System.java:1086)

All that I need - that's correctly initialize library in my project and I don't understand how. I've found a few solutions: * Correct way to add external jars (lib/*.jar) to an IntelliJ IDEA project with suggestion to add dependencies manually in project structure (but when I add it by gradle like described above, libraries are already there)/ * How to bundle a native library and a JNI library inside a JAR? and here suggestion for jar, not war. If I understood correctly, I don't need to unjar library to temp file and load it.

Here is the runner for library:

javac *.java -cp ../../bindings/java/jniSmartIdEngineJar.jar

for image in ../../testdata/*; do 
  config=$(ls ../../data-zip/*.zip) # assuming one file
  for document_types in "rus.passport.*" "mrz.*" "rus.drvlic.*" "*"; do
    LD_LIBRARY_PATH=../../bin LC_ALL=en_US.utf-8 java -cp ../../bindings/java/jniSmartIdEngineJar.jar:. Main "$image" "$config" "$document_types"
  done
done

Please, explain how can I use that external JNI library in my project. And how I can replace that shell runner with gradle config.

PS.: I'm working on Ubuntu and that shell runner works ok. What I'm doing wrong?


Solution

  • Found the solution:

    1. To load external JNI library it must be added in project folder (/lib/ for example)
    2. JAR wrapper file (swig generated in my case) must be added as a dependency (right click on jar > add as a library or manually in project structure modules Dependencies). In my case I'm using gradle

          compile fileTree(include: ['*.jar'], dir: 'lib')
      

      in dependencies {} block

    3. After that classes from jar will be available in project and all that we need - add native library. There are few ways: set -Djava.library.path=... as JVM run argument,or configure it on the fly like described below: https://habr.com/en/post/118027/ Another way - just place library in system folder or add new directory in system variable (Path on Windows or LD_LIBRARY_PATH on linux). Explanation here: How should I load native libraries for JNI to avoid an UnsatisfiedLinkError?

    Regarding to shell script: In p.1 we add jar-wrapper in our classpath LD_LIBRARY_PATH for linux must be specified or another described solution where jvm will be able to find native library.

    And the last - SmartId Reader hasn't ubuntu sdk, so centOS package isn't working on ubuntu (jvm SIGSEGV error, Problematic frame: ld-linux-x86-64.so). I just using windows sdk, it solved issue.