Description
During the investigation of #214, it turned out that the only way to use a JNI library in notebook is to call System.load()
or System.loadLibrary()
inside the library itself. In some cases (see RDKit) library authors don't do it and don't provide any methods for native library loading.
In "general" project setup all dependencies (user code and libraries) are loaded with one URL classloader, so the user may call System.load()
in their code, and it works. In kernel, library dependencies are loaded with URL classloader, and snippets loaded with another org.jetbrains.kotlin.scripting.compiler.plugin.impl.CompiledScriptClassLoader
. So, loaded JNI wrapper just can't see the native definitions which were loaded by System.load()
call in the snippet code.
I propose the following solution for this problem:
- Define a new file annotation
@file:NativeLibrary(path: String)
- On this annotation, compile a single class with the following code:
object Loader_$id {
fun load() = System.load("$path")
}
- Load this class with the same URL classloader with which all other dependencies are loaded. To do it, just add the path to the compiled class to
addedClasspath
list inorg.jetbrains.kotlinx.jupyter.dependencies.JupyterScriptDependenciesResolverImpl#resolveFromAnnotations
. - Before cell execution but after the dependencies were loaded execute snippet
Loader_$id.load()
After all, it allows to write the code like this:
@file:DependsOn("<jni-wrapper.jar>")
@file:NativeLibrary("<native-library.dll>")
// some code that uses methods from <jni-wrapper.jar>
Current workaround for this problem is to make up a wrapper for the native library with the wrapper inside it, see the warpper for RDKit here