在Tomcat中共享JNI库(.so)- UnsatisfiedLinkError

3
我有一个JNI库(.so),被两个部署在Tomcat7中的Web应用程序共享。我使用System.loadLibrary在第一个被部署的Web应用程序中加载该库,然后在第二个Web应用程序中检查是否已经加载过,如果已经加载则不再加载(尝试在两个应用程序中都加载它时,我遇到了UnsatisfiedLinkError——该库已被另一个类加载器加载)。我可以在第一个应用程序中调用任何本地库方法,但是在第二个应用程序中,我却收到了UnsatisfiedLinkError(其中包含我正在尝试调用的方法名称)。
我已经没有什么想法了。有任何建议吗?我在Stack Overflow上尝试了大多数解决方案。 谢谢。
编辑: 是的,我尝试将库添加到tomcat lib文件夹中,并从那里加载它。最初它在bin文件夹中,但是出现了相同的问题。
4个回答

2

当您尝试加载已经加载了另一个Web应用程序的库时,就会发生这种情况。Tomcat为每个Web应用程序使用单独的类加载器,并且不允许您通过另一个类加载器将相同的本机库加载到JVM中超过一次。

如果有任何消耗JNI的共享jar文件,请将其移动到sharedlib.so。将系统路径添加到sharedlib中。

export LD_LIBRARY_PATH=/path/to/whereyourlinklibrary

编写一个简单的类,使您能够在Tomcat启动时加载共享库。只需编译此类并将其放置在Tomcat lib文件夹中即可。

package msm;
public class DLLBootstrapper {

     static {
      System.loadLibrary("sharedlib");
     }

     public static void main(String args[]) {
      System.out.println("Loaded");
     }

    }

现在,您可以从任何Web应用程序中加载此类(可能是在启动侦听器中)。

Class.forName("msm.DLLBootstrapper");

准备就绪!


而且,.class文件DLLBootstrapper应该放在tomcat的lib文件夹中,对吗? - Ovi Faur
我应该将 .class 文件打包成一个 jar 文件并放在 tomcat 的 lib 文件夹中吗? - Ovi Faur
是的。你应该把它放在Tomcat的lib文件夹中。当Tomcat启动时,它会自动加载它。 - kuhajeyan
应该导出为 .class 还是导出为 jar 包? - Ovi Faur
一切看起来都很好,直到我尝试使用加载的库中的方法,仍然会出现未满足的链接错误。在DLL加载器中加载的DLL正常,classForName也返回正确。只是有些困惑。 - Ted Herrlich
显示剩余2条评论

0
你尝试过将共享的JNI库放在服务器的lib目录中吗?这样它应该被所有部署的Web应用程序共享。

还是用System.loadLibrary从那里加载它?还是只是让它留在那里,让Tomcat处理所有这些? - Ovi Faur
System.loadLibrary("libraryName"); - Ankit Tripathi
是的,我尝试过了。还是一样的结果。最初是由ContextInitializer从一个.jar文件中提取出来并放置在tomcat/bin文件夹中的。 - Ovi Faur
你不仅需要将库放在“lib”中,还需要将加载它的代码放在那里,这样它才会被Tomcat共享类加载器加载,而不是Web应用程序类加载器加载(正如你已经发现你不能加载两次,另一个Web应用程序无法访问它)。 - user2543253
没有读这篇博客文章,但看起来不错。你必须自己调用 System.loadLibrary(),但必须确保它不是来自 Web 应用程序而是来自共享代码。 - user2543253
显示剩余2条评论

0

您需要从服务器类加载器中加载任何本机代码,而不是 Web 应用程序类加载器。我建议编写一个监听器,将共享对象的二进制图像加载到 VM 中。这只会发生一次。请参见 AprLifecycleListener 以了解如何正确执行此操作。它包括一个 JNI 组件,很可能正好代表您的情况。

共享对象必须驻留在${catalina.home}/lib中,并且LD_LIBRARY_PATH必须指向它。


0

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