Monthly Archives: May 2016

Building OSGi Bundles with Apache Maven

It seems that still today some developers think that creating an OSGi bundle is complicated – in contrast to simply creating a jar. Well, an OSGi bundle is a jar. The factor which distinguishes the two is that an OSGi bundle has additional manifest entries. Adding those using the right tooling is really simple.

The more difficult part is creating good modules: clearly separating public API from the implementation and correctly version the API based on the changes you made. But this is a general problem not related to OSGi at all and applies to any Java coding. There are some simple guidelines:

  • Leverage packages – put the API in a different package than your implementation. It’s also good still to use a package name for your implementation which makes clear that this is the implementation, e.g. by using impl as one part of the package name.
  • Start private and only make public if necessary. Once something is public, you never can make it private without breaking your clients. Starting private makes things easier.
  • Use semantic versioning. Whenever you change your public API, make sure to correctly increase the package version depending on the type of change you made.

Again, these are general guidelines and have nothing to do with OSGi. But OSGi uses headers in the manifest of your jar to know which packages from inside the jar are public API and the version of those packages. This is the export package header. Packages not listed there are private and not accessible to other modules. The other important header is the import package header, specifying which other packages this module is using. This usually comes with a version range, like a bundle is using package foo.bar version 1.1 or higher up to but not including 2.0.

Therefore, the only required thing to make a bundle out of your jar is more or less calculating these export and import packages headers and adding them to the manifest. But don’t fear, the tooling does this automatically for you. Let’s build a bundle/jar.

Using Apache Maven to Create a OSGi Bundle

There is always the year old debate on which build tool is the best one, I will not get into this discussion. I simply use what I know best and what I’m forced to use anyway: Apache Maven.

By default, Maven is not adding the additional manifest information to the jar, but there are different plugins for Maven available. For this tutorial, I’ll use the newer bnd Maven Plugin from the bnd project.

Add the following two plugin configurations to your pom. It’s usually a good idea to put this into your parent pom:

  <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>
 <plugin>
     <groupId>org.apache.maven.plugins</groupId>
     <artifactId>maven-jar-plugin</artifactId>
     <configuration>
         <useDefaultManifestFile>true</useDefaultManifestFile>
     </configuration>
 </plugin>

The first adds the bnd maven plugin which creates the manifest data and the second instructs the default jar plugin to use the manifest created by the bnd plugin. And that’s it. If you now build your jar project using Maven you’ll have additional manifest entries in there.

Standard OSGi Bundle Headers

The first entries are standard headers which make your jar a bundle and contain some metadata:

  • Bundle-ManifestVersion: This header is required and marks the jar as a bundle, the value is always 2 (newer OSGi specifications might add more features in which case the number would be increased).
  • Bundle-Name: A human readable name for your bundle. This defaults to your project name from the pom.
  • Bundle-SymbolicName: In combination with the bundle version (see below) the symbolic name provides a key to uniquely identify a bundle. This name should be based on the reverse domain name convention and defaults to the artifact id of your project. I’ll talk a little bit more about this soon.
  • Bundle-Version: The version of your bundle, this defaults to the version of your project.

There is no magic in the above entries and having them in your jar does not hurt at all. As mentioned above, the symbolic name should be based on the reverse domain name convention. Usually in the Maven world you follow this rule for your group id, like org.mycompany.myproject and then use a simple name for the artifact id like scheduler-api. However a good practice is to use a full qualified name for the artifact id (which usually means you prefix the artifact id with the group id), e.g. org.mycompany.myproject.scheduler-api. Why is this a good idea? It has nothing to do with OSGi and simply makes sure that the final name of the artifact is fully qualified. If you don’t add the group id to the artifact id, the jar filename would be scheduler-api-1.2.3.jar – which is not very descriptive and gives you no clue what this thing is (and it might clash with other projects using the same artifact id).

The bundle version as any version in software development should increase with continued development/releases. For OSGi this information together with the symbolic name provides a unique key for a bundle and allows OSGi to verify if this exact bundle (name and version) is already installed in the OSGi framework. On a technical level, the bundle version information is more a marketing version.

Public API

More important than versioning your bundle (which still is important), it’s more important to correctly version your public API which might be used by others. For this add the following dependency to your pom:

    <dependency>
        <groupId>org.osgi</groupId>
        <artifactId>osgi.annotation</artifactId>
        <version>1.0.0</version>
        <scope>provided</scoped>
    </dependency>

The above dependency adds some annotations to your project – using the scope “provided” is a good practice as this avoids dragging in transitive dependencies.

Your public API should be in a separate Java package than your implementation – again this is a general good style and not tied to OSGi at all. If you want to export a package to be used by others, add a package-info.java file within your package:

@org.osgi.annotation.versioning.Version("1.0")
package org.osoco.software.samples.guessinggame;

If it’s the first version of your package, simply use 1.0 as the version – from now on once you have released this API as version 1.0, follow semantic versioning. Build your project again and you will see a header similar to this:

Export-Package                org.osoco.software.samples.guessinggame;version="1.0"

As you can see creating the export is really easy, just use the above annotation and done. And the imports are even easier. The plugin analyses your classes and calculates the imports for you. For example for my sample project it looks like this:

Import-Package       javax.servlet;version="[3.1,4)",
                     javax.servlet.http;version="[3.1,4)",
                     org.osoco.software.samples.guessinggame;version="[1.0,1.1)"

And that’s it, your jar is now a bundle and can be used in any OSGi installation as a first class citizen. It’s really simple – and in 96% of your cases the automatic calculation by the tooling is sufficient. There are only rare and special cases where you want to have them differently. In that case, you can configure the plugin accordingly.

Migrating from the Apache Felix SCR Annotations to the OSGi Declarative Services Annotations

The Java annotations of the Apache Felix SCR Plugin were one of the first options to use annotations to create the descriptors for OSGi Declarative Services components and OSGi metatype descriptions for the configuration of such components. With the OSGi R6 release from 2015, the annotations of the OSGi specifications provide the same functionality and go even beyond.

Migrating from the Apache Felix SCR Annotations

Whenever there are competing solutions for the same problem, the question of which one to use arises. In this case, the answer is clear: use the official annotations from the OSGi specifications – for one, they are defined in a standard, but equally important these annotations support all features of Declarative Services R6 – while the Apache Felix annotations only support R5 and it is very unlikely that they will be updated. And that’s the other reason why you should not use the Apache Felix annotations – they are in maintenance mode. Of course, should any bug or problem arise, this will be fixed.

The second question usually is: Should I migrate existing code? There is no need to bulk migrate existing code. As said, the Apache Felix plugin is still maintained, is open source and simply works. However, if you have to touch your code, migrating the annotations might be a good idea. It also gives you the chance to simplify your code by leveraging the R6 features. That said, if you do so, this of course ties your implementation to an R6 implementation of Declarative Services, like Apache Felix SCR, version 2.0 or higher.

Mapping to OSGi Declarative Services Annotations

The below table gives you an overview of how to map the annotations.

Annotation Mapping

Apache Felix SCR AnnotationDescriptionOSGi Declarative Services AnnotationDescription
@ComponentThe @Component annotation marks a Java class to be used as a component.@ComponentThis is more or less a strict one-to-one replacement. Only difference is the default behavior for services. See below.
@ServiceMarks the component as a service and optionally lists the provided services (classes)@ComponentThe OSGi annotation has a service attribute which should be used to list the provided services. Be careful, if your component should not provide any service, set this attribute to an empty array.
@ReferenceReference to services, can be used on unary fields or on class level with bind/unbind methods.@ReferenceField references can directly be migrated, for event based references (methods), the @Reference annotation must be put on the bind method. In addition, more options for field references exist.
@Activate, @Deactivate, and @ModifiedMark a method being the activation, deactivation or modified method@Activate, @Deactivate, and @ModifiedStraight one-to-one migration.
@PropertyConfiguration properties and metatype.Component Property Type and metatype annotationsInstead of using a set of @Property annotations, the configuration is described through an annotation (component property type) and OSGi Metatype annotations can be used to add the metatype info.
Mapping between Apache Felix SCR Annotations and OSGi Declarative Services Annotations

The above table is of course just a short reference. You’ll find more information in the OSGi specifications and in my Declarative Services Tutorials: Part I, Part II, and Part III.