Blogs

SpringSource Blog

Creating OSGi bundles

Costin Leau

When approaching OSGi, one of the first concepts that have to be learned is the notion of a bundle. In this entry, I'd like to take a closer look of what a bundle actually is and how a vanilla jar can be transformed into an OSGi bundle.  So, without further ado,

What is a bundle?

The OSGi spec describes the bundle as a "unit of modularization" that "is comprised of Java classes and other resources which together can provide functions to end users.". So far so good, but what exactly is a bundle? Quoting the spec again:

a bundle is a JAR file that:

  • Contains [...] resources
  • Contains a manifest file describing the contents of the JAR file and providing information about the bundle
  • Can contain optional documentation in the OSGI-OPT directory of the JAR file or one of its sub-directories

In short, a bundle = jar + OSGI information (specified in the JAR manifest file – META-INF/MANIFEST.MF), no extra files or predefined folder layout are required. This means that all it takes to create a bundle from a jar, is to add some entries to the JAR manifest.

OSGi metadata

The OSGi metadata is represented by manifest entries that dictate to the OSGi framework what the bundle provides or/and requires. The specification indicates around 20 manifest headers but we will just take a look at the ones you are most likely to use.

Export-Package

As the name implies, this header indicates what packages (available in the bundle) are exported so they can be imported by other bundles. Only the packages specified by the header will be exported, the rest will be private and will not be seen outside the containing bundle.

Import-Package

Similar to Export-Package, this header indicates the packages that are imported by a bundle. As well, only the packages specified by this header will be imported. By default, imported packages are mandatory – the importing bundle will fail to start, if the imported package is not available.

Bundle-SymbolicName

The only required header, this entry specifies a unique identifier for a bundle, based on the reverse domain name convention (used also by the java packages).

Bundle-Name

Defines a human-readable name, without spaces, for this bundle. Setting this header is recommend since it can provide a shorter, more meaningful information about the bundle content then Bundle-SymbolicName.

Bundle-Activator

The BundleActivator is an OSGi specific interface that allows Java code to be notified when a bundle is started or stopped by the OSGi framework. The value of this header should contain a fully qualified name of the activator class which should be public and contain a public constructor without any arguments.

Bundle-Classpath

This header is handy when the jar contains embedded libraries or class packages under various folders, by extending the default bundle classpath (which expects the classes to be available directly under the jar root).

Bundle-ManifestVersion

This little known header indicates the OSGi specification to use for reading this bundle. 1 indicates OSGi release 3 while 2 OSGi release 4 and later. Since 1 is the default version, it is strongly recommended to specify this header since an OSGi release 4 bundle will not work as expected under OSGi release 3.

Below is an example, taken from Spring 2.5.x core bundle manifest that uses some of the headers mentioned above:

Bundle-Name: spring-core
Bundle-SymbolicName: org.springframework.bundle.spring.core
Bundle-ManifestVersion: 2
Export-Package:org.springframework.core.task;uses:="org.springframework.core,org.springframework.util";version=2.5.1 org.springframework.core.type;uses:=org.springframework.core.annotation;version=2.5.1[...]
Import-Package:org.apache.commons.logging,edu.emory.mathcs.backport.java.util.concurrent;resolution:=optional[...]

Most of the time used on OSGi metadata is likely to be spent on Export/Import package entries as they describe the relationship between bundles (that is, between your modules). When it comes to packages, nothing is implicit – only packages that are mentioned are imported/exported, the rest aren't. This applies also to sub-packages: exporting org.mypackage will export just this package and nothing else (like org.mypackage.util). Same goes for importing – even if a package is available inside the OSGi space, it will not be seen by a certain bundle unless it is explicitly imported by it.

To summarize, if a bundle A exports package org.mypackage and bundle B wants to consume it, then the META-INF/MANIFEST.MF for bundle A should specify the package inside its Export-Package header, while bundle B should include it in its Import-Package entry.

Package consideration

While exporting is fairly straight forward, importing is slightly more complex. It is common for applications to degrade nicely by searching the environment for certain libraries and using only what is available, or for libraries to include code that is not used by the user. Such examples include logging (using JDK 1.4 or Log4j), regular expressions (Jakarta ORO or JDK 1.4+) or concurrent utilities (java.util in JDK 5 or backport-util-concurrent library for JDK 1.4).

In OSGi terms, relying on a package based on its availability translates to an optional Package-Import. You have already seen such a package in the previous example:

Import-Package: [...]edu.emory.mathcs.backport.java.util.concurrent;resolution:=optional

Since in OSGi, multiple versions of the same class can exist, it is best practice to specify the version of the class package both when exporting and importing a package. This is done through the version attribute which is added after each package declaration. The version format supported by OSGi is <major>.<minor>.<micro>.<qualifier> where major, minor and micro are numbers and qualifier is alphanumeric.

The meaning of the version is completely up to the bundle provider however, it is recommend to use a popular numbering scheme such as the one from Apache APR project where:

  • <major> – indicates a significant update which doesn't guarantee any compatibility
  • <minor> – indicates an update which preserves compatibility with older minor versions
  • <micro> – represents an insignificant update from the user point of view which is perfectly compatible both forwards and backwards
  • <qualifier> – is a user defined string – it is not widely used and can provide an additional label to the version number, such as the build number or target platform without a standardized meaning

The default version (if the attribute is missing) is "0.0.0".

While the exported packages have to indicate a specific version, the importers can indicate a range using the mathematical interval notation – for example

[1.0.4, 2.0) will match version 1.0.42 and upwards up to 2.0 (excluding). Note that an specifying only a version instead of an interval will match all packages that are at greater or equal then the specified version, that is :

Import-Package: com.mypackage;version="1.2.3"

is equivalent to

Import-Package: com.mypackage;version="[1.2.3, ∞)"

As a last tip, make sure to always use quotes when specifying an version, whether it is a range or not.

Working with OSGi metadata

Now that we have some information on what bundles are, let's see what tools we can use for osgi-fying an existing jar:

by hand

This do-it-yourself approach is discouraged as typos and extra spaces can easily sneak in and render the manifest useless. Even when working with a smart editor, the manifest format itself can cause some problems since it has a limit of 72 spaces per line which, if broken, can cause some cryptic problems. Manually creating or updating the jar is not a good idea since the jar format requires the META-INF/MANIFEST.MF entry to be the first one in the archive - if it's not, even though it's present in the jar, the manifest file will not be read. The manual approach is really recommended for cases where there are no other alternatives.

However, if one really wants/needs to work directly with the manifest, then a editor that can deal with UNIX/DOS spaces should be used along with a proper jar creation utility (such as the jar tool that comes with the JDK) to cope with all the MANIFEST requirements.

Bnd

Bnd stands for BuNDle tool and is a nice utility created by Peter Kriens (OSGi Technical Officer) that "helps [...] create and diagnose OSGi R4 bundles". Bnd parses the java classes to understand the available and imported packages so it can create the equivalent OSGi entries. Bnd offers a series of directives and options which can customize the resulting artifact. The nice thing about bnd.jar itself is that is can be run from command line, by Ant through dedicated tasks or integrated into Eclipse as a plug-in.

Bnd can create jars from the classes available on the classpath or inside Eclipse projects or can osgi-fy existing jars by adding the needed OSGi artifacts. Additionally, it can print and verify the OSGi information about the given jar making it quite a powerful, yet simple to use tool.

First time users, can use Bnd to see what OSGi manifest will be added to a vanilla jar. Let's pick a vanilla jar such as c3p0 (which is an excellent connection pool library) and issue a print command:

java -jar bnd.jar print c3p0-0.9.1.2.jar

The output is fairly big and contains of several sections:

  1. Generic manifest information:

    [MANIFEST c3p0-0.9.1.2.jar]
    Ant-Version Apache Ant 1.7.0
    Created-By 1.5.0_07-87 (&quot;Apple Computer, Inc.&quot;)
    Extension-Name com.mchange.v2.c3p0
    Implementation-Vendor Machinery For Change, Inc.
    Implementation-Vendor-Id com.mchange
    Implementation-Version 0.9.1.2
    Manifest-Version 1.0
    Specification-Vendor Machinery For Change, Inc.
    Specification-Version 1.0
    
  2. Package Information:
    com.mchange.v2.c3p0.management   com.mchange.v1.lang com.mchange.v2.c3p0
                                                                   com.mchange.v2.c3p0.impl com.mchange.v2.debug
                                                                   com.mchange.v2.log com.mchange.v2.management
                                                                   java.sql
                                                                   javax.management
                                                                   javax.sql
    

    which indicates specifies the packages discovered in the jar (on the left side) and its imports (on the right side).

  3. Possible Errors – normally these indicate the packages that were not found in the classpath but were referenced by other classes:
     One error 1 : Unresolved references to
    [javax.management, javax.naming, javax.naming.spi, javax.sql, javax.xml.parsers, org.apache.log4j, org.w3c.dom]
    by class(es) on the Bundle-Classpath[Jar:c3p0-0.9.1.2.jar]: [...]
    

    .
    This section is a good indication on what packages the given jar imports.

Let's OSGify the artifact by using

java -jar bnd.jar wrap c3p0-0.9.1.2.jar 

this will create a new archive with the exact content as the original jar but with a modified MANIFEST.MF that will contain the OSGi imports marked as optional. The current Bnd tool saves the archive with a .jar$ extension while previous versions used .bar instead.

We might chose to tweak the jar by adding versioning, excluding some exported packages and marking some imported packages as mandatory (such as javax.sql in this case). To do that, we'll create a c3p0-0.9.1.2.bnd file as follows:

version=0.9.1.2
Export-Package: com.mchange*;version=${version}
Import-Package: java.sql*,javax.sql*,*;resolution:=optional
Bundle-Version: ${version}
Bundle-Description: c3p0 connection pool
Bundle-Name: c3p0

Notice that for version we used variable substitution. To hook in the properties file, use the following command line:

java -jar bnd.jar wrap -properties c3p0-0.9.1.2.bnd c3p0-0.9.1.2.jar

I've used the .bnd extension since by default, the Bnd ant tasks will look for this file during execution.

To use Bnd tool with ant, one can just import the tasks provided out-of-the-box and invoke them during jar creation:

<taskdef resource="aQute/bnd/ant/taskdef.properties" classpath="${lib.dir}/bnd/bnd.jar"/>
...
<bndwrap definitions="${basedir}/osgi/bnd" output="${dist.dir}">
   <fileset dir="${dist.dir}" includes="*.jar"/>
</bndwrap>

Note that usually, a move task follows to copy the .jar$ or .bar artifact over the original jar.

Bundle plug-in for Maven

For Maven, Apache Felix Bundle Plug-in provides nice integration between Bnd and Maven 2. Since the Maven POM contains additional information about the project, the Bnd plug-in can populate other fields of the manifest automatically, such as Bundle-License or Bundle-Version, using the the project properties.

The official documentation explains the usage in detail so I will not replicate it here.

To convert our c3p0 library, I will use a simple Maven 2 pom which will download the original artifact and then wrap it as a bundle:

<?xml version="1.0" encoding="UTF-8"?>
<project
        xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>my.company</groupId>
    <artifactId>c3p0.osgi</artifactId>
    <packaging>bundle</packaging>
    <version>0.9.1.2-SNAPSHOT</version>
    <name>c3p0.osgi</name>

    <properties>
        <export.packages>${export.package}*;version=${unpack.version}</export.packages>
        <import.packages>*</import.packages>
        <private.packages>!*</private.packages>
        <symbolic.name>${pom.groupId}.${pom.artifactId}</symbolic.name>
        <embed-dep>*;scope=provided;type=!pom;inline=true</embed-dep>
        <unpack-bundle>false</unpack-bundle>
    </properties>

    <build>
    <plugins>
     <plugin>
        <groupId>org.apache.felix</groupId>
        <artifactId>maven-bundle-plugin</artifactId>
        <version>1.2.0</version>
        <configuration>
            <unpackBundle>${unpack.bundle}</unpackBundle>
            <instructions>
                <Bundle-Name>${artifactId}</Bundle-Name>
                <Bundle-SymbolicName>${symbolic.name}</Bundle-SymbolicName>
                <Bundle-Description>${pom.name}</Bundle-Description>
                <Import-Package>${import.packages}</Import-Package>
                <Private-Package>${private.packages}</Private-Package>
                <Include-Resource>${include.resources}</Include-Resource>
                <Embed-Dependency>${embed-dep}</Embed-Dependency>
                <_exportcontents>${export.packages}</_exportcontents>
            </instructions>
        </configuration>
        <extensions>true</extensions>
     </plugin>
    </plugins>
    </build>

    <dependencies>
      <dependency>
        <groupId>c3p0</groupId>
        <artifactId>c3p0</artifactId>
        <version>0.9.1.2</version>
        <scope>provided</scope>
      </dependency>
    </dependencies>
</project>

Packaging the project will create an OSGi bundle, identical in content with the original one expect for the MANIFEST.MF which will contain the OSGi entries.

Notice the usage of properties for externalizing the plug-in configuration. When working with multiple project inside a module, the properties allow the generic plug-in configuration to be placed inside the top level pom and for each sub-module to override it by specifying different properties values. A live example of such a setup is the Spring-DM osgi repository.

It is important to know that Bnd considers all the classes available on the classpath when creating a bundle. When using Bnd from the command line, as in the previous example, the classpath is formed only by a jar so no extra classes besides c3p0 exist. However, when using a building tool such as Maven or Ant, the classpath is considerably larger – in this case, based on your Export/Import package directives, Bnd might add or discard classes from the resulting jar. To prevent this, make sure to use patterns matching just the actual package included, i.e: com.mchange.* instead of *.

Custom, in-house tool

Another approach, though not likely to be encountered, is to create a customized tool, usually based on bytecode analysis. Such an utility can be highly customized for certain environments to gain speed or minimize the memory print or to support additional heuristics or configuration files. Spring Dynamic Modules contains such an internal, ASM-based byte-code parser for its test framework, to efficiently create on-the-fly MANIFEST.MFs.

For generic use however, Bnd tool (either vanilla or through its Maven integration) provides much more options and works quite fast. In fact, the more general the usage becomes, the more likely Bnd will fit the bill through its high customizability.

Using existing OSGi repository

These being said, before wrapping existing libraries as OSGi bundles, check whether somebody already did it for you. You can do that by checking one of the existing OSGi repositories:

OSGi Bundle Repository (ORB) – OSGi Alliance bundles repository which provides "a federated collection of bundles".

Eclipse Orbit – which contains artifacts usable inside Eclipse environment. Since Eclipse uses Equinox, the artifacts might contain Equinox specific Manifest entries

Apache Felix Commons – which aims at "sharing [...] bundlized artifacts"

Apache OSGified projects – a simple page that indicates the Apache Commons projects that have or are about to include the OSGi manifest entries into their official distribution.

Hopefully, with the community help, many of the popular Java libraries out there will be OSGi-friendly by default and using a separate repository or wrapping the jar will not be necessary. Until then you can help out by providing patches to the projects you use or by simply asking for this feature.

Before ending this post, I would like to invite all of you interested in OSGi and Spring Dynamic Modules to an upcoming webinar next week, on Wednesday Feb 27th, that will cover the core OSGi concepts and Spring DM fundamentals.

Similar Posts

Share this Post
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • DZone
  • LinkedIn
  • Slashdot
  • Technorati
  • Twitter
 

14 responses


  1. Sad … eventually a spec for good component development arrives in Java, and first thing that happens people automate away the declaration of imports and exports, the thoughtful specification of which is the key to a component model.


  2. unbundled, utilities like bnd make working with manifests a lot easier and simpler then it would be by hand. It's very easy to misspell a package or add an extra space before or after the comma and end up with invalid manifest. Even when using a tool, it's still up to the user to decide what packages have to exported and imported.
    At the end of the day, it's about choice – use (or not) whatever tool makes more sense for you.


  3. [quote post="277"]Sad … eventually a spec for good component development arrives in Java, and first thing that happens people automate away the declaration of imports and exports, the thoughtful specification of which is the key to a component model.[/quote]

    Maybe it's just me being lazy, but I don't see the advantage of figuring out all the dependencies manually and putting them in my manifest over doing it automatically and analyzing the result. Don't get me wrong, being thoughtful about adding dependencies is very important, however it is possible to be thoughtful without doing everything by hand.


  4. Please do not use urchinTracker to track link clicks! It breaks accessibility, and does not work at all with FireFox AdBlock (all links are hidden)…


  5. It's all about choices and I really enjoy edit manifest with support of Eclipse PDE. This is really helps to minimize depencies. Adding some import or export by hand its naturally to consider wherether actually those depencies are needed. As for BND it is questionable why learn some cryptic format (with 5 lines for example) that generates another cryptic format (resulting 11 lines for example).


  6. [quote comment="99287"]As for BND it is questionable why learn some cryptic format (with 5 lines for example) that generates another cryptic format (resulting 11 lines for example).[/quote]
    Eugene, I'm not sure what you mean by cryptic: BND uses for input a simplified manifest syntax (based on regular expressions) and for output a valid OSGi manifest.


  7. [quote post="277"]BND uses for input a simplified manifest syntax (based on regular expressions) and for output a valid OSGi manifest.[/quote]
    That's exactly what I mean. I'm more comfortable with plain validated at design time OSGi manifest, then simplified format with more rules that make it concise. [quote post="277"]manifest format itself can cause some problems since it has a limit of 72 spaces per line which, if broken, can cause some cryptic problems[/quote]. Оnly non-validated manifest format can cause some problems, and in that case bnd have little advantages, being more synthetic. Choises…


  8. I worked with bnd during my diploma thesis 1 1/2 years ago and i had the same feelings when i started with it. But due to the fact that the project i was working on was not structured in a set of subprojects so that i could have used eclipse pde for my developing but rather consisted of one huge classpath during build time i tried out bnd and it worked well. Now looking back i should have done the bnd step much earlier than trying to do it only the pde way.

    Bnd can be used also to create and register ServiceComponent descriptions. With your dynamic modules in mind it would be nice to supplement these best practices with a bnd file that also includes the spring-osgi header and refister config files.

    Nice overview Costin


  9. Very interesting indeed, thanks Costin. In fact, for example, your article made me discover the "72 bytes" rule for the manifest (http://java.sun.com/j2se/1.4.2/docs/guide/jar/jar.html#Manifest Specification).
    However, I wasn't able to find any reference to the MANIFEST.MF file that has to be the first entry on the jar file?
    Actually, I had a look in my jars and saw not all are following this rule. I also looked in the OSGi core spec and didn't find anything related to it. I was surprised about this one because I didn't have much problems running Eclipse RCP with some bad jars this way.

    One last thing: in Eclipse, it seems like bundle naming (not the things inside the MANIFEST, I mean the jar name) is important. For example, we recently had a problem with a bundle we created "asm.jar". I seem to remember we just renamed it so that the bundle was then named something like: Bundle-SymbolicName_Bunddle-Version.jar and then all went fine…
    Though I didn't find any rules about bundle naming in the spec, is there any??? Could someone confirm or not that Equinox has (or may have in some situations) a special behaviour about bundle management?

    Cheers.


  10. [quote comment="102133"]However, I wasn't able to find any reference to the MANIFEST.MF file that has to be the first entry on the jar file?
    Actually, I had a look in my jars and saw not all are following this rule. I also looked in the OSGi core spec and didn't find anything related to it. I was surprised about this one because I didn't have much problems running Eclipse RCP with some bad jars this way.[/quote]

    I believe the "rule" of MANIFEST.MF being the first entry is more of a best practice. If I recall correctly, it depends on how the jar is created or read. Archives created with the standard Jar tool will always have the /META-INF/MANIFEST.MF as a first entry (try jar-tvf some.jar). If memory serves me right, Equinox had no problems reading Jars with misplaced MANIFEST.MF but Knopflerfish had, since it relied on JarInputStream which, I think, doesn't read the MANIFEST unless it is the first entry.
    From a stream point of view, this makes sense since the manifest is supposed to describe the archive structure and thus, should be the first available data in the stream, before the actual content.

    At the end of day, it's not about the OSGi spec but rather on how the jar is actually handled at low-level. One difference between the platforms, that we discovered early on in this area for example, was the difference in which folder entries are handled. An archive can contain just the selected files but not the folder entries.
    That is, only:
    /a/b/c/file.txt

    vs:

    /a/
    /a/b/
    /a/b/c/
    /a/b/c/file.txt

    Depending on the tool that you create/use, such 'synthethic' entries can be created automatically for you or not – however, without them, searching a bundle jar can yield different results depending on whether the platform will consider the folder entries even if they don't exist or will use only what's available in the archive.

    [quote comment=102133"]
    One last thing: in Eclipse, it seems like bundle naming (not the things inside the MANIFEST, I mean the jar name) is important. For example, we recently had a problem with a bundle we created "asm.jar". I seem to remember we just renamed it so that the bundle was then named something like: Bundle-SymbolicName_Bunddle-Version.jar and then all went fine…
    Though I didn't find any rules about bundle naming in the spec, is there any??? Could someone confirm or not that Equinox has (or may have in some situations) a special behaviour about bundle management?[/quote]
    I am not aware of such a requirement in the OSGi spec or Equinox – in fact, the jars that we use in our tests have very different naming patterns and none of them resembles the one you mentioned. Maybe you are using Eclipse PDE where the jars have a certain pattern?
    The actual jar name can be useful only for the bundle location (populated during install) so on bundle refresh (unless a different location in the manifest is specified), the jar is read again. But this shouldn't be affected by a ".jar" style.

    Hope this helps


  11. "But due to the fact that the project i was working on was not structured in a set of subprojects so that i could have used eclipse pde for my developing but rather consisted of one huge classpath during build time"

    Yeah, it looks like bnd works great for projects that are not structured… but I'm disappointed that it doesn't work for projects that _are_ structured.

    Working with Eclipse and separating your bundles into logical projects with individual manifests is, in my opinion, cleaner and more modular. However, I have not found bnd to support this. Hiding packages (so that two classes with the same name and package could potentially exist in separate bundles and not exported) is a feature of OSGi and using one giant unstructured src/bin folder destroys that!

    In my perfect world:
    1) The manifest should be maintained in only _one_ spot. Generating it every time doesn't work well with version control or for just running your src straight out of the box with an IDE. Saying, "Oh, by the way… you have to run these ANT targets before you open Eclipse" isn't fun.
    2) Compiling a bundle with ANT would be easy with multiple projects, source folders, and manifests.

    I won't be using bnd as a build tool.


  12. I created bundles for third party jars using BND plugin. All packages are exported. But, still when I try to use it, I get "The constructor is not accessible due to restriction on required library ". Any idea on why this happens and how to avoid this?

    Thanks,

    - Raja.


  13. I created bundles for third party jars using BND plugin. All packages are exported. But, still when I try to use it, I get "The constructor is not accessible due to restriction on required library ". Any idea on why this happens and how to avoid this?


  14. Nice write-up, Costin, thanks!

12 trackbacks

Leave a Reply