在 OSGi 包中加载 DLL(使用 JNA)
OSGi 找不到我的 DLL 文件,我似乎不明白为什么。
目前,我的包的根目录中有 DLL 文件 (foo.dll
),我也尝试将它放在 libs
目录中。
有问题的包的清单看起来像这样:
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: foobundle
Bundle-SymbolicName: com.foo.bar
Bundle-Version: 1.0.0
Bundle-Vendor: me
Import-Package: com.sun.jna,
com.sun.jna.ptr,
com.sun.jna.win32
Export-Package: com.foo.bar
Bundle-NativeCode: foo.dll;
osname=WindowsXP;
processor=x86
然后在我的 JNA 接口中,我执行 loadLibrary (根据文档):
public interface MyFooInterface extends com.sun.jna.Library{
static final MyFooInterface INSTANCE = (MyFooInterface)com.sun.jna.Native.loadLibrary("foo", MyFooInterface .class);
// specific interface defs here...
}
然后在另一个类中,我尝试使用 JNA 接口,
// ...code
int var = MyFooInterface.INSTANCE.bar();
// ...more code
我通过另一个包提供了 JNA (导出 com .sun.jna 和上面导入的其他包),但也尝试使用此处定义的包将其打包(在这种情况下将其添加到类路径等)。
我还尝试指定 Bundle-NativeCode: /foo.dll
。
同样有趣的是,这些是相关的 OSGi 属性(我使用 getprop
提取的)
org.osgi.framework.os.name=WindowsXP
org.osgi.framework.processor=x86
即使在这一切之后(以及我所做的每一次尝试),我总是会遇到以下错误(以及堆栈跟踪)未显示):
java.lang.UnsatisfiedLinkError: Unable to load library 'foo': The specified module could not be found.
...那么我错过了什么?
编辑:我还应该注意到,我已经测试并成功地测试了 JNA 接口代码和它作为 JUnit 测试程序的一部分与之对话的 DLL。
编辑 2:将此代码添加到调用库的类中似乎允许 JNA 找到该库(当稍后调用 Native.loadLibrary
时)。看来我应该能够根据清单中的 Bundle-NativeCode 指令避免此调用。显然,一旦加载库, Native.loadLibrary 就会获取它的现有实例,但我不想依赖这种特定于订单的策略。
static{
System.loadLibrary("foo");
}
OSGi cannot find my DLL file, and I can't seem to figure out why.
Currently I have the DLL file (foo.dll
) at the root of my bundle, I've also tried having it in a libs
directory.
The Manifest for the bundle in question looks something like this:
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: foobundle
Bundle-SymbolicName: com.foo.bar
Bundle-Version: 1.0.0
Bundle-Vendor: me
Import-Package: com.sun.jna,
com.sun.jna.ptr,
com.sun.jna.win32
Export-Package: com.foo.bar
Bundle-NativeCode: foo.dll;
osname=WindowsXP;
processor=x86
Then in my JNA interface I perform a loadLibrary (as per the documentation):
public interface MyFooInterface extends com.sun.jna.Library{
static final MyFooInterface INSTANCE = (MyFooInterface)com.sun.jna.Native.loadLibrary("foo", MyFooInterface .class);
// specific interface defs here...
}
Then in another class I attempt to use the JNA interface
// ...code
int var = MyFooInterface.INSTANCE.bar();
// ...more code
I have JNA supplied via another bundle (which exports com.sun.jna and the other packages imported above), but have also tried packaging it with the bundle defined here (and added it to the classpath in that case, etc.).
I've also tried specifying Bundle-NativeCode: /foo.dll
.
Also of interest, these are the relevant OSGi properties (which I pulled up using getprop
)
org.osgi.framework.os.name=WindowsXP
org.osgi.framework.processor=x86
Even after all this (and with every trial I made) I always end up with the following error (and a stack trace not shown):
java.lang.UnsatisfiedLinkError: Unable to load library 'foo': The specified module could not be found.
...so what am I missing?
Edit: I should also note that I've tested and had success the JNA interface code and the DLL that it talks to as part of a JUnit Test program.
Edit 2: Adding this code to the class that's calling the library seems to allow JNA to find the library (when Native.loadLibrary
gets called later). It seems I should be able to avoid this call based on the Bundle-NativeCode directive in the Manifest. Clearly once the library is loaded Native.loadLibrary grabs the existing instance of it, but I'd prefer not to depend on this very order-specific tactic.
static{
System.loadLibrary("foo");
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
问题在于专门的 JNA loadLibrary 调用,它不被 OSGi 识别。当您从 OSGi 捆绑包调用 loadLibrary 时,它将使用 OSGi 类加载器(捆绑包感知)来查找 DLL 的位置,在这种情况下,将其从捆绑包中提取出来并使其可通过 System.loadLibrary() 加载。针对特定位置进行呼叫。
既然这个 JNA 似乎 (a) 不支持 OSGi,并且 (b) 多余,为什么不直接使用 System.loadLibrary() 呢?
如果您需要同时编写这两个内容,请在 BundleActivator 中的包的 start() 方法中执行 System.loadLibrary(),这将引入本机库(您可能希望确保如果无法加载该包,则该包无论如何都无法启动)。
The problem is the specialised JNA loadLibrary call, which is not OSGi aware. When you invoke loadLibrary from an OSGi bundle, it will use the OSGi classloader (which is bundle aware) to find where the DLL is, and in this case, extract it out from the bundle and make it loadable via the System.loadLibrary() call against a specific location.
Since this JNA seems to be (a) not OSGi aware, and (b) superflous, why not just use System.loadLibrary() instead?
If you need to write both, then perform a System.loadLibrary() in the bundle's start() method in the BundleActivator, which will bring the native library in (you probably want to ensure that if it can't be loaded, the bundle can't be started in any case).
查看 JNA 的文档,它指出:
jna.library.path
系统属性设置为目标库的路径。此属性与java.library.path
类似,但仅适用于 JNA 加载的库。PATH
,在 Linux 上是LD_LIBRARY_PATH
,在 OSX 上是DYLD_LIBRARY_PATH
。因此,为了解决这个缺点,您可以解析库的绝对路径并加载它。
假设它是 Eclipse 的标准类加载器,您可以执行 ClassLoader.findLibrary() ,它应该在包中找到本地库。
Looking at JNA's documentation, it states:
jna.library.path
system property to the path to your target library. This property is similar tojava.library.path
but only applies to libraries loaded by JNA.PATH
on Windows,LD_LIBRARY_PATH
on Linux, andDYLD_LIBRARY_PATH
on OSX.So to get around this shortcoming you could resolve the the absolute path of the library and load that.
Assuming that its Eclipse's standard class loader, you can do
ClassLoader.findLibrary()
which should find the local library in the bundle.我建议您尝试将 dll 打包为 jar:
并将 jar 作为常规库加载。
I suggest you try to package the dll as a jar:
and the load the jar as a regular lib.
最好将
com.sun.jna
包放在org.osgi.framework.system.packages.extra
列表中,因为本机库(例如 <由com.sun.jna.Native 内部使用的 code>jnidispatch.dll
)只能加载一次,而 OSGi 类加载器可以多次加载类。It's a good idea to have the
com.sun.jna
packages in yourorg.osgi.framework.system.packages.extra
list, because the native libraries (such asjnidispatch.dll
used internally bycom.sun.jna.Native
) can only be loaded once, whereas OSGi classloaders can load classes multiple times.