To support loading native code in OSGi bundles, the framework defines a specific header, Bundle-NativeCode, which defines the libraries that are available to the bundle via the System.loadLibrary
call.
The Bundle-NativeCode
header defines one or more native libraries and a clause which states what operating systems and processor architectures are valid for each library. Calls to System.loadLibrary
will then look for libraries mentioned in this list and use only those found for the appropriate architecture. In effect, the Bundle-NativeCode
header replaces the java.library.path
property.
In the prior example, the maths
library was used for performing calculations. In an OSGi bundle, this could be packaged with the bundle itself and referred to via a manifest header:
Bundle-NativeCode: native/maths.dll
When calling System.loadLibrary("maths")
on Windows, the native/maths.dll
library will be automatically extracted to a suitable location on the filesystem and passed to the operating system for loading.
If a bundle is being designed to support more than one operating system, then the library needs to be qualified accordingly. To do this, clauses can be appended to each native library to determine which osname or operating system they can run on:
Bundle-NativeCode: native/maths.dll;osname=win32, native/libmaths.dylib;osname=macosx, native/libmaths.so;osname=linux
Now when run on a Windows platform, System.loadLibrary("maths")
will load the native/maths.dll
library; for Linux platforms, native/libmaths.so
will be loaded instead; and for Mac OS X, native/libmaths.dylib
will be used.
The OSGi R6 specification translates requirements in the Bundle-NativeCode
header to a set of generic requirements on the osgi.native
namespace. An osname=win32
clause is automatically translated to a Require-Capability: osgi.native.osname~=win32
clause. This allows resolvers to choose the right dependency automatically. Eclipse Luna has an OSGi R6 compatible version of Equinox.
Although each operating system has its own naming convention (and thus the libraries can all be in the same directory in this instance), this approach doesn't work when providing multiple libraries for the same operating system. Although Mac OS X can create multi-architecture bundles, other operating systems are restricted to a single-processor architecture per library.
If the bundle needs to support both 32-bit and 64-bit operating systems, two versions of the library are required and the processor attribute disambiguates between them:
Bundle-NativeCode: native/x86/maths.dll;osname=win32;processor=x86, native/x86_64/maths.dll;osname=win32;processor=x86_64, native/libmaths.dylib;osname=macosx, native/x86/libmaths.so;osname=linux;processor=x86, native/x86_64/libmaths.so;osname=linux;processor=x86_64
The correct version of the maths
library is loaded depending upon whether it is a 32-bit (x86
) or 64-bit (x86_64
) processor.
A side effect of the Bundle-NativeCode
header is that if it is present, then it must have an associated entry that matches the operating system. If it is not, then the bundle will fail to resolve.
As a result, specifying the following will mean that the bundle cannot resolve on platforms other than Mac OS X:
Bundle-NativeCode: native/libmaths.dylib;osname=macosx
This is desirable in cases where there is a dependency on a framework specific to an operating system (such as a dependency on Cocoa.framework
), but in other cases, a native library can be useful to accelerate certain actions but will work fine without the native library.
To declare that the native library is optional, place an asterisk (*
) as another option in the Bundle-NativeCode
header:
Bundle-NativeCode: native/libmaths.dylib;osname=macosx,*
This special syntax indicates that the bundle should still resolve normally even if the operating system isn't Mac OS X, but with the expectation that System.loadLibrary
will only be called when running on a Mac OS X platform; or alternatively, that the code can handle an UnsatisifedLinkError
when calling the method.
When providing multiple libraries on a single platform, they all need to be referenced on the same clause. The OSGi resolution iterates through the clauses in order, and stops at the first one matching the current environment. As a result, this will never load the maths
library:
Bundle-NativeCode: native/libother.dylib;osname=macosx, native/libmaths.dylib;osname=macosx
Even though this looks like it should work, calls to System.loadLibrary("other")
will work as expected, while calls to System.loadLibrary("maths")
will fail.
To define multiple libraries, concatenate them into the same clause:
Bundle-NativeCode: native/libother.dylib;native/libmaths.dylib;osname=macosx
Now, the resolution of other
and maths
will work on the macosx
platform.
If there are multiple libraries with the same name in the same clause, then only the first one is loaded. For example, if a separate debug version of the maths
library was used and placed in a separate directory, then there would be no way to load it:
Bundle-NativeCode: native/libmaths.dylib;debug/libmaths.dylib;osname=macosx
Since folders are ignored and cannot be supplied as part of the System.loadLibrary
call, any reference to maths
will always result in the first native/libmaths.dylib
library.
The right way to resolve this is to either rename the library, for example, libmaths-debug.dylib
, or use an alternative mechanism, such as the filter or fragment solutions discussed later in this chapter.
It is possible to attach additional filters and constraints on loading the native libraries in an OSGi bundle. There are a number of standard attributes that can be specified along with a generic filter:
osname
: This is the name of the operating system (win32
, macosx
, or linux
)osversion
: This is the version number of the operating system (8.1
, 10.9
, or 3.2
)processor
: This is the processor type (x86
, x86_64
)language
: This is the ISO language code, in case the DLL has textual contentselection-filter
: This is an OSGi LDAP filter, which can be applied for other system propertiesFor a full list of supported values, see the OSGi specification or on the OSGi website at http://www.osgi.org/Specifications/Reference.
The selection-filter
can be used to provide a specific debug variant of an available library by specifying a system property for the Java VM. For example, if two versions of a library were required, one with debugging symbols, then the filter can be set as follows:
Bundle-NativeCode: native/libmaths.dylib;selection-filter=(!(debug=true)) debug/libmaths.dylib;selection-filter=(debug=true)
Running the VM with a -Ddebug=true
flag would result in the debug libraries being loaded by System.loadLibrary
, whereas with any other value (or unset), the normal one would be used.
selection-filter
can also be used to test for other conditions, for example:
Bundle-NativeCode: native/libmaths.dylib;selection-filter=(file.encoding=UTF-8)
This can be used to selectively load different libraries based on the windowing system installed:
Bundle-NativeCode: native/libgtk.so;selection-filter=(osgi.ws=gtk), native/libcocoa.dylib;selection-filter=(osgi.ws=cocoa), native/mfc.dll;selection-filter=(osgi.ws=win32)