Skip to content

Commit 703ac42

Browse files
author
Kevin Sapper
committed
Problem: Fatal exception when NativeLoader loads same jar-packaged library twice
Solution: Using System.loadLibrary you can load the same library as many times as you like because subsequent calls will be ignored. But this is only true if you try to load the same library from the same file. Because the NativeLoader copies the jar-packaged library to a random directory in the temp dir we're loading the same library from different files which causes the java process to abort fatally. Therefore we will load the libraries with our own static library loader wrapper that will only call the NativeLoader if that library has not been load previously. And because we generate the wrapper in every zproject generated jni libraray the jvm will simply pick up the first one it encounters hence there's no need to have a common dependency where that loader is located.
1 parent a1ec69f commit 703ac42

File tree

1 file changed

+43
-12
lines changed

1 file changed

+43
-12
lines changed

zproject_java.gsl

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,10 @@ task generateJniHeaders(type: Exec, dependsOn: 'classes') {
170170
'src/main/java/$(name_path)/$(name:pascal).java'$(last ()?? ""? ",")
171171
. endfor
172172
]
173-
commandLine("javac", "-h", "$nativeIncludes", "-classpath", "$classpath:$appclasspath", *jniClasses)
173+
def utilityClasses = [
174+
'src/main/java/org/zeromq/tools/ZmqNativeLoader.java'
175+
]
176+
commandLine("javac", "-h", "$nativeIncludes", "-classpath", "$classpath:$appclasspath", *jniClasses, *utilityClasses)
174177
}
175178
tasks.withType(Test) {
176179
systemProperty "java.library.path", "/usr/lib:/usr/local/lib:$projectDir"
@@ -755,6 +758,37 @@ popd
755758
.endif
756759
.endmacro
757760

761+
.macro generate_native_loader ()
762+
. directory.create ("$(topdir)/src/main/java/org/zeromq/tools")
763+
. output "$(topdir)/src/main/java/org/zeromq/tools/ZmqNativeLoader.java"
764+
package org.zeromq.tools;
765+
766+
import java.util.Set;
767+
import java.util.HashSet;
768+
import org.scijava.nativelib.NativeLoader;
769+
770+
public class ZmqNativeLoader {
771+
772+
private static final Set<String> loadedLibraries = new HashSet<>();
773+
774+
public static void loadLibrary(String libname) {
775+
if (!loadedLibraries.contains(libname)) {
776+
if (System.getProperty("java.vm.vendor").contains("Android")) {
777+
System.loadLibrary(libname);
778+
} else {
779+
try {
780+
NativeLoader.loadLibrary(libname);
781+
} catch (Exception e) {
782+
System.err.println("[WARN] " + e.getMessage() +" from jar. Assuming it is installed on the system.");
783+
}
784+
}
785+
loadedLibraries.add(libname);
786+
}
787+
}
788+
789+
}
790+
.endmacro
791+
758792
.macro generate_class_in_java (class)
759793
. directory.create ("$(topdir)/src/main/java/$(name_path)")
760794
. output "$(topdir)/src/main/java/$(name_path)/$(my.class.name:pascal).java"
@@ -764,7 +798,7 @@ $(project.GENERATED_WARNING_HEADER:)
764798
package org.zeromq.$(project.prefix:c);
765799

766800
import java.util.stream.Stream;
767-
import org.scijava.nativelib.NativeLoader;
801+
import org.zeromq.tools.ZmqNativeLoader;
768802
. for project.use
769803
. if count (project->dependencies.class, class.project = use.project) > 0
770804
import org.zeromq.$(use.project).*;
@@ -777,27 +811,23 @@ implements AutoCloseable\
777811
. endif
778812
{
779813
static {
780-
if (System.getProperty("java.vm.vendor").contains("Android")) {
781-
System.loadLibrary("$(project.prefix:c)jni");
782-
} else {
783-
Stream.of(
814+
Stream.of(
784815
. for project.use
785816
"$(use.prefix:c)",
786817
. endfor
787818
"$(project.prefix:c)"
788819
)
789820
.forEach(lib -> {
790821
try {
791-
NativeLoader.loadLibrary(lib);
822+
ZmqNativeLoader.loadLibrary(lib);
792823
} catch (Exception e) {
793824
System.err.println("[WARN] " + e.getMessage() +" from jar. Assuming it is installed on the system.");
794825
}
795826
});
796-
try {
797-
NativeLoader.loadLibrary("$(project.prefix:c)jni");
798-
} catch (Exception e) {
799-
System.exit (-1);
800-
}
827+
try {
828+
ZmqNativeLoader.loadLibrary("$(project.prefix:c)jni");
829+
} catch (Exception e) {
830+
System.exit (-1);
801831
}
802832
}
803833
public long self;
@@ -990,6 +1020,7 @@ public class $(my.class.name:pascal)Test {
9901020
project.topdir = "bindings/jni"
9911021
directory.create (topdir)
9921022

1023+
generate_native_loader ()
9931024
for project.class
9941025
resolve_class (class)
9951026
if class.okay

0 commit comments

Comments
 (0)