I’m currently doing some development on a large RCP application. One recent task required using a Windows DLL to open an email message through MAPI, which is made pretty easy with OSGi. But I encountered a few issues that I’m sure will bite other people.
now provides some support for resolving and loading shared libraries/DLLs in Java applications. A bundle declaratively specifies its DLLs and associated platform requirements in their bundle manifest using the Bundle-NativeCode header. This header value is then used when loading a DLL through System.loadLibrary().
TheBundle-NativeCode header specifies a number of comma-delimited clauses. For example:
Bundle-NativeCode: lib/win32/x86/http.dll; lib/win/zlib.dll; processor=x86; osname=win32, lib/macosx/libhttp.dylib; lib/macosx/libzlib.jnilib; osname=macosx; processor=x86; processor=x86_64, *
A clause specifies one or more DLLs. A clause is matched to the current platform using a set of parameters (e.g., processor and osname, but also osversion, language, or a selection-filter). Parameters of different types are ANDd together; parameters of the same type are ORd together, which can be tricky. Only a single clause is matched for the current platform, so multiple libraries must be specified within the same clause.
The trailing “*” indicates that the DLLs are optional. It means that the bundle will still resolve even if there are no other matching clauses. If this optional specifier is used, it must come last.
The power of Bundle-NativeHeader is two fold. First, a bundle won’t resolve if there is no matching clause for the current platform. If you don’t include the optional specifier (the trailing asterisk), your code is pretty much guaranteed to never throw an UnsatisfiedLinkError. Second, OSGi will resolve library name (e.g., “http”) to the appropriate DLLs for the platform, and will manage the DLL loading and unloading.
I’ve been bitten by a couple of “gotchas” with the Bundle-NativeCode header.
Gotcha #1: System.loadLibrary() must still be called
I incorrectly thought that OSGi would load the specified DLLs automatically. Unfortunately your Java code must still explicitly load the libraries using System.libraryLoad(). Happily you needn’t specify the path location or the platform-specific prefixes or extensions. So using the example above, the code could just call:
[In hindsight, this makes sense: you wouldn't want DLLs loaded unnecessarily.]
Gotcha #2: Dependencies aren’t traced
OSGi doesn’t resolve DLL dependencies through the bundle’s dependencies. This hasn’t bitten me personally, but I have come across a few reports online.
Gotcha #3: “Missing host Bundle-NativeCode_0.0.0″: not as hazardous as may appear
The lonely asterisk (“*”) is not actually considered as a clause. It’s simply a configuration parameter that causes the bundle to continue to be resolved even if there are no matching clauses.
When there are no matching clauses, Equinox displays a message like:
!SUBENTRY 2 jmapi 2 0 2011-03-28 10:23:27.633 !MESSAGE Missing host Bundle-NativeCode_0.0.0.
This is an error message only if the asterisk has not been specified! If the asterisk has been specified, then it’s merely an informational message; the bundle may still be successfully resolved.
This differentiation has caused me grief on several occasions now as I assumed such a message was an error and the cause of subsequent errors.
The OSGi Core Specification describes the interpretation of the Bundle-NativeCode header. In OSGi 4.2, it’s specifically in §3.7 and §3.10.