This post cover the basics of generating an OSGi bundle with maven, and compares two maven plugin options.

By David Norris on
Post Banner

Overview

This post will cover the differences between the existing options for building OSGi bundles using Maven and provide some useful examples. Historically, there has only been one maven plugin option for building OSGi bundles, the Apache Felix maven-bundle-plugin; however, another plugin option has recently become available (see announcement), the bnd-maven-plugin. Both plugins rely on bnd (often referred to as the swiss army knife of OSGi) under the hood, but they diverge a bit in the details.

Differences between maven-bundle-plugin and bnd-maven-plugin

Maven LifeCycle Differences

The first notable difference between the two plugins is in how they hook into the Maven build lifecycle. The felix maven-bundle-plugin extends the default maven lifecylcle with its own “lifecycle enhancement”, and enables a custom packaging type of “bundle”. Its noteworthy that it is possible to avoid the use of the custom packaging type (see here), but this is the expected default.

The bnd-maven-plugin is designed instead to hook into the default Maven “process-classes” lifecycle and does not require any custom packaging type. This was a conscious decision on the part of the implementors and was meant to mitigate some of the issues that can be caused by the use of the custom “bundle” packaging type (e.g. interoperability with other Maven plugins).

BND Configuration format (pom vs bnd)

The second major difference between the two plugins is in how bnd configuration is expected to be passed to bnd. The felix maven-bundle-plugin is typical of most Maven plugins in accepting its configuration through the pom.xml.

Example:

<plugin>
    <groupId>org.apache.felix</groupId>
    <artifactId>maven-bundle-plugin</artifactId>
    <extensions>true</extensions>
    <configuration>
        <instructions>
            <!-- This is included as a best practice, by default this plugin 
            will expose <groupId>.<artifactId>.* -->
            <Export-Package></Export-Package>
        </instructions>
    </configuration>
</plugin>  

The bnd-maven-plugin allows the options to either use an external bnd file, or embed bnd directives directly into the plugin configuration of the pom.xml.

Example of embedding bnd content in pom.xml:

<plugin>
    <groupId>biz.aQute.bnd</groupId>
    <artifactId>bnd-maven-plugin</artifactId>
    <version>3.2.0</version>
    <executions>
        <execution>
            <goals>
                <goal>bnd-process</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <bnd>
            <![CDATA[
            -exportcontents: ${packages;VERSIONED}
            ]]>
        </bnd>
    </configuration>
</plugin>

This example configuration will parse org.osgi.annotation.versioning.Version annotations on package-info.java classes to simplify management of package versioning info.

BND Defaults

The third significant difference between the two plugins is in the details of the bnd configuration defaults.

Felix maven-bundle-plugin bnd defaults
 Bundle-SymbolicName: <groupId>.<artifactId>(if artifactId is prefixed by groupId, it is not included)
 Bundle-Name:        <name> (<name> is <artifactId> when not specified)
 Bundle-Version:      <version>
 Import-Package:      *
 Export-Package:     <groupId>.<artifactId>.* (unless Private-Package is set)
 Bundle-Description:  <description>
 Bundle-License:     <licenses>
 Bundle-Vendor:      <organization>
 Include-Resource:    ${project.build.resources}

source

bnd-maven-plugin bnd defaults
 Bundle-SymbolicName: <artifactId>
 Bundle-Name:         <name> (<name> is <artifactId> when not specified)
 Bundle-Version:     <version>
 Import-Package:      *
 Export-Package:      exports nothing by default
 Bundle-Description:  not included by default
 Bundle-License:        not included by default
 Bundle-Vendor:        not included by default
 Include-Resource:    ${project.build.resources}
 Private-Package:       *

source

List of differences (see above for details)
  1. Bundle-SymbolicName
  2. Export-Package/Private-Package (This is the most significant functional difference between the defaults)
  3. Bundle-Description
  4. Bundle-License
  5. Bundle-Vendor

Example Projects

For both examples, we will reuse the hello-world SCR compoment java code. The goal will be to show how to package up the very same code using each plugin. For the source, you can checkout the parent project code from github.

bnd-maven-plugin project
Tree View

Tree View

bnd file (Optional)

A bnd file by the name of “bnd.bnd” can be added to the root directory if custom bnd directives are required for your project; however, in this instance we do not require any configuration since SCR annotations are now processed by default by bnd.

pom.xml
<?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/xsd/maven-4.0.0.xsd">
 
    <modelVersion>4.0.0</modelVersion>
 
    <groupId>com.stackleader</groupId>
    <artifactId>com.stackleader.training.osgi.bnd.plugin</artifactId>
    <packaging>jar</packaging>
    
    <parent>
        <groupId>com.stackleader</groupId>
        <artifactId>com.stackleader.training.osgi</artifactId>
        <version>0.0.1</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.osgi</groupId>
            <artifactId>org.osgi.compendium</artifactId>
        </dependency>
        <dependency>
            <groupId>org.osgi</groupId>
            <artifactId>org.osgi.annotation</artifactId>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>biz.aQute.bnd</groupId>
                <artifactId>bnd-maven-plugin</artifactId>
                <version>3.2.0</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>bnd-process</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.0.0</version>
                <configuration>
                    <archive>
                        <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
MANIFEST.MF
Manifest-Version: 1.0
Bundle-SymbolicName: com.stackleader.training.osgi.bnd.plugin
Archiver-Version: Plexus Archiver
Built-By: dcnorris
Bnd-LastModified: 1464969212686
Bundle-ManifestVersion: 2
Import-Package: org.slf4j;version="[1.7,2)"
Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.5))"
Service-Component: OSGI-INF/com.stackleader.training.osgi.scr.HelloScr
 World.xml
Tool: Bnd-3.2.0.201605172007
Bundle-Name: com.stackleader.training.osgi.bnd.plugin
Bundle-Version: 0.0.1
Private-Package: com.stackleader.training.osgi.scr
Created-By: 1.8.0_74 (Oracle Corporation)
Build-Jdk: 1.8.0_74
Felix maven-bundle-plugin project
Tree View

Tree View

pom.xml
<?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/xsd/maven-4.0.0.xsd">
 
    <modelVersion>4.0.0</modelVersion>
 
    <groupId>com.stackleader</groupId>
    <artifactId>com.stackleader.training.osgi.felix.plugin</artifactId>
    <!--    Note the non-standard packaging type of 'bundle'-->
    <packaging>bundle</packaging>
    
    <parent>
        <groupId>com.stackleader</groupId>
        <artifactId>com.stackleader.training.osgi</artifactId>
        <version>0.0.1</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
   
    <dependencies>
        <dependency>
            <groupId>org.osgi</groupId>
            <artifactId>org.osgi.compendium</artifactId>
        </dependency>
        <dependency>
            <groupId>org.osgi</groupId>
            <artifactId>org.osgi.annotation</artifactId>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <extensions>true</extensions>
                <configuration>
                    <instructions>
                        <!-- This is included as a best practice, by default this plugin 
                        will expose all <groupId>.<artifactId>.* packages. In our case, we do not want to expose 
                        any packages.-->
                        <Export-Package></Export-Package>
                    </instructions>
                </configuration>
            </plugin>  
        </plugins>
    </build>
</project>
MANIFEST.MF
Manifest-Version: 1.0
Bnd-LastModified: 1464969332232
Build-Jdk: 1.8.0_74
Built-By: dcnorris
Bundle-ManifestVersion: 2
Bundle-Name: com.stackleader.training.osgi.felix.plugin
Bundle-SymbolicName: com.stackleader.training.osgi.felix.plugin
Bundle-Version: 0.0.1
Created-By: Apache Maven Bundle Plugin
Import-Package: org.slf4j;version="[1.7,2)"
Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.5))"
Service-Component: OSGI-INF/com.stackleader.training.osgi.scr.HelloScrWo
 rld.xml
Tool: Bnd-3.0.0.201509101326
© Copyright 2023 by David Norris.