Spring Netbeans 运行 上的应用程序 java.lang.UnsatisfiedLinkError

Spring application java.lang.UnsatisfiedLinkError on Netbeans run

我们有一个使用 OR-Tools 的应用程序,它由几个模块组成(maven,所有这些都是 Spring 引导模块),特别是:

  1. 优化器模块,通过以下代码加载or-tools DLL:
public class Optimizer {

    static {
        try {
            // extracts .dll from or-tools-windows jar and copies it into C:\users\my-user\AppData\Local\Temp\temp_ortools
            // calls System.load("C:\users\my-user\AppData\Local\Temp\temp_ortools\jniortools.dll")
            carregaBibliotecasORTools(); 
        } catch (Exception e) {
            log.info(e.getMessage());
        } catch (Error e) {
            log.info(e.getMessage());
        }
    }

    public static OptimizerData getOptimizerData() {
       
        OptimizerData optimizerData = new OptimizerData();
        System.out.println("Instantiating Solver");
        optimizerData.solver = MPSolver.createSolver("SCIP");
        System.out.println("Solver Instantiated");
        
        return optimizerData;
        
    }

}
  1. 服务模块,使用:
@Service
public class PlanningService{

    public void executePlan() {
       
        OptimizerData optimizerData = Optimizer.getOptimizerData();

        ...
        
    }

}
  1. web 模块(spring-boot-starter-web 依赖项),使服务可通过控制器使用

我们已成功运行以下情况的代码:

  1. 运行 优化器模块上的测试方法(在 netbeans 上),它调用 getOptimizerData 方法
  2. 使用 3 个模块(通过在 netbeans 上构建 web 模块)生成一个 fat jar 并 运行ning 这个 fat jar。端点调用工作正常

但是,当我们 运行 Netbeans 上的 Web 模块时,调用端点时会显示以下错误消息: 2021-05-09 22:59:38.587 ERROR 19300 --- [nio-8020-exec-8] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler dispatch failed; nested exception is java.lang.UnsatisfiedLinkError: com.google.ortools.linearsolver.main_research_linear_solverJNI.MPSolver_createSolver(Ljava/lang/String;)J] with root cause

一些重要的观察结果:

  1. 考虑到 fat jar 工作正常,这似乎是 netbeans 特有的问题
  2. 我们执行了以下方法 (System.load) 以验证库是否确实已加载:
    public static void listAllLoadedNativeLibrariesFromJVM() {
        
        ClassLoader appLoader = ClassLoader.getSystemClassLoader();
        ClassLoader currentLoader = LibraryLoader.class.getClassLoader();

        ClassLoader[] loaders = new ClassLoader[] { appLoader, currentLoader };
        for (int i=0; i<loaders.length; i++) {
            final String[] libraries = ClassScope.getLoadedLibraries(loaders[i]);
            for (String library : libraries) {
                System.out.println("Loader " + loaders[i].getClass().getName() + " : " + library);
            }
        }
        
    }

此方法的结果是:

  1. 优化器模块中的测试文件(运行良好):
Loader sun.misc.Launcher$AppClassLoader : C:\Program Files\Java\jdk1.8.0_281\jre\bin\zip.dll
Loader sun.misc.Launcher$AppClassLoader : C:\Users\my-user\AppData\Local\Temp\temp_ortools\jniortools.dll
Loader sun.misc.Launcher$AppClassLoader : C:\Program Files\Java\jdk1.8.0_281\jre\bin\zip.dll
Loader sun.misc.Launcher$AppClassLoader : C:\Users\my-user\AppData\Local\Temp\temp_ortools\jniortools.dll
  1. 通过编译 web 模块生成的 Fat .jar(运行良好):
Loader sun.misc.Launcher$AppClassLoader : C:\Program Files\Java\jre1.8.0_281\bin\zip.dll
Loader sun.misc.Launcher$AppClassLoader : C:\Program Files\Java\jre1.8.0_281\bin\management.dll
Loader sun.misc.Launcher$AppClassLoader : C:\Program Files\Java\jre1.8.0_281\bin\net.dll
Loader sun.misc.Launcher$AppClassLoader : C:\Program Files\Java\jre1.8.0_281\bin\nio.dll
Loader sun.misc.Launcher$AppClassLoader : C:\Users\my-user\AppData\Local\Temp\temp_ortools\jniortools.dll
Loader org.springframework.boot.loader.LaunchedURLClassLoader : C:\Program Files\Java\jre1.8.0_281\bin\zip.dll
Loader org.springframework.boot.loader.LaunchedURLClassLoader : C:\Program Files\Java\jre1.8.0_281\bin\management.dll
Loader org.springframework.boot.loader.LaunchedURLClassLoader : C:\Program Files\Java\jre1.8.0_281\bin\net.dll
Loader org.springframework.boot.loader.LaunchedURLClassLoader : C:\Program Files\Java\jre1.8.0_281\bin\nio.dll
Loader org.springframework.boot.loader.LaunchedURLClassLoader : C:\Users\my-user\AppData\Local\Temp\temp_ortools\jniortools.dll
  1. Netbeans 'run' Web 模块上的命令
Loader sun.misc.Launcher$AppClassLoader : C:\Program Files\Java\jdk1.8.0_281\jre\bin\zip.dll
Loader sun.misc.Launcher$AppClassLoader : C:\Program Files\Java\jdk1.8.0_281\jre\bin\management.dll
Loader sun.misc.Launcher$AppClassLoader : C:\Program Files\Java\jdk1.8.0_281\jre\bin\net.dll
Loader sun.misc.Launcher$AppClassLoader : C:\Program Files\Java\jdk1.8.0_281\jre\bin\nio.dll
Loader sun.misc.Launcher$AppClassLoader : C:\Users\my-user\AppData\Local\Temp\temp_ortools\jniortools.dll
Loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader : C:\Program Files\Java\jdk1.8.0_281\jre\bin\zip.dll
Loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader : C:\Program Files\Java\jdk1.8.0_281\jre\bin\management.dll
Loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader : C:\Program Files\Java\jdk1.8.0_281\jre\bin\net.dll
Loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader : C:\Program Files\Java\jdk1.8.0_281\jre\bin\nio.dll
Loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader : C:\Users\my-user\AppData\Local\Temp\temp_ortools\jniortools.dll

通过比较 listAllLoadedNativeLibrariesFromJVM 调用的最后 2 个输出,加载的 fat jar 和 netbeans 之间存在细微差别 类,具体而言:

  1. Netbeans 运行 : Loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader : C:\Users\my-user\AppData\Local\Temp\temp_ortools\jniortools.dll
  2. 胖罐子运行 : Loader org.springframework.boot.loader.LaunchedURLClassLoader : C:\Users\my-user\AppData\Local\Temp\temp_ortools\jniortools.dll

根据这些日志,我们确定以下依赖项是导致问题的原因:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <scope>runtime</scope>
    <optional>true</optional>
</dependency>

此依赖项允许 Spring 应用程序在代码更改的情况下更快地重新启动。通过删除此依赖项,应用程序 运行s 在 Netbeans 上没有任何问题。

欢迎任何关于如何使 Spring Boot Devtools 使用静态加载的 .dll 库的见解。