Open schmir opened 8 years ago
I don't know of any mechanism by which you can isolate JNI libraries in the JVM. This means that the code that calls loadLibrary()
needs to check to make sure the library isn't already loaded before it loads it. I don't know of any robust way to manage native libraries, so Boot just stays out of your way there.
Do you have a recommendation for how to deal with these issues?
Sorry, but I don't have any recommendation, nor do I have any clue about classloaders. I've tried to search google and it looks like running JNI libs in Tomcat also may trigger that error.
I've tried to load the library directly from build.boot, but that didn't help.
If time permits I'll take another look at that issue. At least I can learn something about classloaders.
I've tried to ignore errors by wrapping the loadLibrary code in a try/catch block. That didn't work since I've later got an UnsatisfiedLinkError, when trying to call functions from the package.
Then I've took a look at the classloader implementation in openjdk. It looks like the above error is thrown here:
It's possible to modify the loadedLibraryNames, with the following code:
(defn filter-loaded-library-names
[pred]
(let [loaded-library-names (.getDeclaredField java.lang.ClassLoader "loadedLibraryNames")
loaded-library-names-vector (do (.setAccessible loaded-library-names true)
(.get loaded-library-names java.lang.ClassLoader))
filename-idx (mapv vector loaded-library-names-vector (range))]
(doseq [[filename idx] (->> filename-idx
(remove (comp pred first))
reverse)]
#_(println "REMOVE" filename idx)
(.removeElementAt loaded-library-names-vector idx))))
;: adapt this:
(filter-loaded-library-names (fn [filename] (not (str/index-of filename "sepa-jni"))))
I'm now able to use 'boot watch test' with this workaround.
I'm not sure if it would make sense to have that workaround in boot. At least it may be useful to have a link in the documentation to this issue.
I'm also not sure why I seem to be the first person running into that problem.
On Aug 1, 2016 2:27 PM, "Ralf Schmitt" notifications@github.com wrote:
I've tried to ignore errors by wrapping the loadLibrary code in a try/catch block. That didn't work since I've later got an UnsatisfiedLinkError, when trying to call functions from the package.
Then I've took a look at the classloader implementation in openjdk. It looks like the above error is thrown here:
It's possible to modify the loadedLibraryNames, with the following code:
(defn filter-loaded-library-names [pred](let [loaded-library-names %28.getDeclaredField java.lang.ClassLoader) loaded-library-names-vector (do (.setAccessible loaded-library-names true) (.get loaded-library-names java.lang.ClassLoader)) filename-idx (mapv vector loaded-library-names-vector (range))] (doseq [[filename idx](->> filename-idx %28remove %28comp pred first%29%29 reverse)] #_(println "REMOVE" filename idx) (.removeElementAt loaded-library-names-vector idx)))) ;: adapt this: (filter-loaded-library-names (fn [filename](not %28str/index-of filename))))
I'm now able to use 'boot watch test' with this workaround.
I'm not sure if it would make sense to have that workaround in boot. At least it may be useful to have a link in the documentation to this issue.
I'm also not sure why I seem to be the first person running into that problem.
FWIW I was planning to run into that problem. Glad you got there first, cause I never would have been able to figure it out Thanks!
I ran into a similar problem on a different project. JNI requires that a given library be loaded at most once within a JVM. However, even if the library was previously loaded in the same process, if it was loaded by a different classloader, then the same UnsatisfiedLinkError will result.
The only solution I've ever found is to force the library to load at a very high level classloader, such that all descendants will resolve the JNI classes (and the linked library) via the application classloader.
I have no idea whether this would be possible with pods. Do pods ultimately delegate classloading up to the boot classloader?
@mtnygard Pods don't do anything unusual with class loading, they use normal URLClassLoader
s that do parent-first delegation. The boot.App
class is loaded from a higher-level class loader, and all pods are siblings whose parent class loader is the one that spawned boot.App
.
Perhaps it would be possible to monkey-patch System.loadLibrary(String libname)
in pods? If such a thing is possible then we could perhaps have that method delegate to a similar method on the boot.App
class that can manage the state?
I ran into this same issue while playing around with boot-test in my clj-vulkan-guide project. You can reproduce the issue by checking out the test
branch and running boot test test
.
I'm trying to use boot-test. I'm using clojure 1.8.0, boot 2.6.0 on a jdk 8 on Linux. My project uses a JNI based library and I'm not able to use boot-test together with the watch task. If I run "boot test" everything is fine:
However, I'm not able to use boot watch test:
(which looks good until I change a file)
I've ran into the same issue while trying to use clojurescript with my project. So, running "boot test" manually is not a workaround for me here.