使用 RXTX 部署自包含应用程序

Deploying a self-contained application with RXTX

我有一个现有的应用程序,它通常通过 TCP 与其目标进行通信,但是新的要求规定也可以通过串行 COM 端口建立连接。

目前的应用程序是完全独立的,因为它是一个单一的 jar 文件,最终用户可以将其复制到可能需要的地方,然后双击启动。

RXTX 似乎打破了这个模型,因为它需要额外的 DLLSO 本地插件包含在库路径中;如果无法使用本机 Java 与串行端口通信,我如何打包我的应用程序(使用 ,aven)以便 RXTX 在运行时可用,只需双击 Jar 文件即可。

java.lang.UnsatisfiedLinkError: no rxtxSerial in java.library.path: [__CLASSPATH__] thrown while loading gnu.io.RXTXCommDriver

您需要将库打包到您的 jar 文件中,然后将其解压缩,将其作为文件写入,然后加载它(省略 import 语句和 exception/error 处理):

public class YourClass {
    static {
        // path to and base name of the library in the jar file
        String libResourcePath = "path/to/library/yourLib.";

        // assume Linux
        String extension = "so";

        // Check for Windows
        String osName = System.getProperty("os.name").toLowerCase();
        if (osName.contains("win"))
            extension = "dll";

        libResourcePath += extension;

        // find the library in the jar file
        InputStream is = ClassLoader.getSystemResourceAsStream( libResourcePath );

        // create a temp file for the library
        // (and we need the File object later so don't chain the calls)
        File libraryFile = File.getTempFile("libName", extension);

        // need a separate OutputStream object so we can
        // explicitly close() it - can cause problems loading
        // the library later if we don't do that
        FileOutputStream fos = new FileOutputStream(libraryFile);

        // assume Java 9
        is.transferTo(fos);

        // don't forget these - especially the OutputStream
        is.close();
        fos.close();

        libraryFile.setExecutable(true);
        libraryFile.deleteOnExit();

        // use 'load()' and not 'loadLibrary()' as the
        // file probably doesn't fit the required naming
        // scheme to use 'loadLibrary()'
        System.load(libraryFile.getCanonicalPath());
    }

    ...
}

请注意,您需要为每个 OS 和您支持的体系结构添加您的库版本。这包括 32 位和 64 位版本,因为很可能 运行 64 位 OS.

上的 32 位 JVM

您可以使用 os.arch 系统 属性 来确定您是否在 运行 64 位 JVM 中。如果 属性 中包含字符串 "64",那么您正在 运行 64 位 JVM 中。假设它是 32 位 JVM 是非常安全的,否则:

if (System.getProperty("os.arch").contains("64"))
    // 64-bit JVM code
else
    // 32-bit JVM code

如果您要自定义 class 加载器,您可能还必须使用 YourClass.class.getClassLoader().getResourceAsStream()

注意 OS 和 CPU 的兼容性。您需要编译您的库,以便它们 运行 在旧的 OS 版本和旧的 CPU 上。如果您在新的 CPU 上构建 Centos 7,那么尝试在 Centos 6 或更旧的 CPU 上 运行 的人将会遇到问题。您不希望您的产品在用户身上崩溃,因为您的库收到了非法指令的 SIGILL 信号。我建议在 VM 上构建库,您可以在其中控制构建环境 - 使用旧的 CPU 模型创建 VM 并在其上安装旧的 OS 版本。

您还需要注意挂载 /tmpnoexec 的 Linux 系统。该设置或某些 SE Linux 设置可能会导致共享对象加载失败。