Posted by: Brian de Alwis | March 28, 2011

Bundle-NativeCode: Using platform-specific DLLs from OSGi

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.

Bundle-NativeCode

OSGi 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.

Gotchas

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:

System.loadLibrary("http");

[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.

References

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.

There are a few blog posts describing how to use this facility. They were very helpful!

About these ads

Responses

  1. “OSGi now provides some support for resolving and loading shared libraries/DLLs [...]”
    What does “now” mean? Since 4.3?

    • Good point: it’s not a new feature and has been around since at least 2006!

  2. Hi! Thank you for a good post, it’s very helpful. Thanks to this post, I have been able to bundle native library “sapjco3″.
    But I don’t understand one thing.

    I am importing package com.sap.mw.jco.*; But this package will be available only after System.loadLibrary(), so during it get’s available during runtime. During compilation it’s not available, that’s why my compilation fails on line “import com.sap.mw.jco.*” What am I missunderstanding..? How can I make native libraries available during compilation

    Sorry, if my question is silly.

    • The DLLs are only needed at runtime when the JVM loads the library. The compiler only needs access to the Java classes containing the native methods. If the compile is failing on an import then verify your bundle dependencies and that they’re all exporting the appropriate packages.

      • Thank you for your answer. It turned out that I was using the wrong version of the jar. After I installed the correct library, everything worked perfectly. Thanks again for your article, especially for gotcha#1


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories

Follow

Get every new post delivered to your Inbox.

Join 149 other followers