Java Web Start - 使用另一个本地依赖项加载本地依赖项

8

我正在使用Java Web Start启动一个依赖于第三方本地库的Java应用程序。然后,这些本地库使用LoadLibrary/dlopen加载另一个名为commonLib的本地库作为它们的依赖项。

当不使用Web Start时,当本地库位于同一目录中时,一切正常。

然而,Web Start需要将本地库打包在jar文件中并在jnlp文件中引用,我已经这样做了:

  <!-- Windows OS -->
    <resources os="Windows">
        <nativelib href="native/native-windows.jar" />
    </resource>

  <!-- Linux OS -->
    <resources os="Linux">
        <nativelib href="native/native-linux.jar" />
    </resources>

  <!-- Mac OSX -->
    <resources os="Mac OS X">
        <nativelib href="native/native-osx.jar"/>   
    </resources>

本地库可以正常加载,但无法加载它们的依赖项commonLib - C++ LoadLibrary/dlopen调用失败,因为该文件存在于某个jar/cache文件夹中而不在当前库搜索路径上。

在Windows上,我能够通过在尝试加载JNI库之前在Java中预加载commonLib来解决这个问题,方法如下:

System.loadLibrary("commonLib");
System.loadLibrary("myNativeLib");

然而,这种方法在OS X上不起作用——本地代码中的dlopen失败。显然,如果已经加载了库,dlopen并不聪明,不能避免尝试重新加载库。
是否有一种跨平台的方式可以在Java Web Start中打包和加载依赖于其他本地库的本地库?

这些Jars包含所有相关的本地文件吗? - Andrew Thompson
是的,已经双重检查过了。 - Karel Petranek
1个回答

2

我能找到一个(丑陋的)解决方法。诀窍是将依赖库(commonLib)打包成一个简单的资源jar文件,并将其添加到jnlp文件中:

...
<resources os="Windows">
  <jar href="native/deps-windows.jar" />
</resources>
<resources os="Linux">
  <jar href="native/deps-linux.jar" />
</resources>
<resources os="Mac OS X">
  <jar href="native/deps-osx.jar" />
</resources>
...

第二步是使用Java将这些资源提取到一个临时目录中:
String tmpDir = System.getProperty("java.io.tmpdir");
if (!tmpDir.endsWith("/") && !tmpDir.endsWith("\\"))
    tmpDir += "/";
String resource = "commonDir.dll"; // Adjust accordingly to current OS, omitted for brevity
InputStream is = loader.getResourceAsStream(resource);
if (is == null) {
    // Handle error - resource not found
    return;
}
try {
    FileOutputStream os = new FileOutputStream(tmpDir + resource);
    byte[] buffer = new byte[1024*1024];
    int len = is.read(buffer);
    while (len != -1) {
        os.write(buffer, 0, len);
        len = is.read(buffer);
    }
    os.close();
    is.close();
    System.out.println("Extracted " + resource + " to " + tmpDir);
} catch(IOException ex) {
    // Handle the exception - cannot write to temp directory
    System.out.println("Could not extract resource " + resource + " to " + tmpDir + ": " + ex.getMessage());
}

第三步是要么向本地JNI库通知提取的依赖项的完整路径,要么临时将当前目录设置为临时目录tmpDir,加载JNI库并将其设置回来。这本身就是一个问题——在Java中很难更改当前工作目录。不过,您可以通过创建另一个小型的实用程序JNI库从C中解决这个问题[1]。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接