OSGi Getting Started

By Michael N. Lipp

Mastodon Follow

View GitHub Project

Introduction

Part 1: The container

Part 2: Simplest Bundle

Part 3: Simple Bundle

Part 4: Eclipse (OSGi) Plugin

Part 5: Bndtools

Part 6: Shift in Perspective

Part 7: Modules and Services

Part 8: Accessing a Service

Part 9: Tracking a Service

Part 10: Using a Service

Part 11: Providing a Service

Interlude: Cleaning up

Part 12: Configuration Admin

Part 13: Service Components

Part 14: Repositories

Part 15: Versions

Repositories

If you’re used to working with Maven repositories such as “Maven Central”, you may have wondered why we need those complicated repository configurations for using bnd/bndtools.

The reasons are completely different ideas about what a repository contains. Maven repositories are comprised of artifacts that can be identified by their Maven Coordinates. Apart from a very generic type attribute that allows to distinguish code-jars from jars containing e.g. JavaDoc, nothing is known about the artifacts. It’s up to the user to find out which artifact in a repository provides whatever is needed and create a dependency on it.

OSGi has a strong component orientation. An important point about components is that they are interchangeable with other components that provide the same interface1. OSGi projects should therefore not refer to required resources by specifying an artifact that they depend on, but rather by stating that they depend on a component that implements a specific interface or feature.

A special form of specifying a dependency the “OSGi way” is the Import-Package Header in the MANIFEST.MF that we have seen before. Let’s re-use the header from the part “Accessing a Service”:

Import-Package: org.osgi.framework;version="[1.6,2)"

It is internally transformed to the more general form of a requirement:

Require-Capability: osgi.wiring.package;\
	filter:="(&(osgi.wiring.package=org.osgi.framework)\
	  (&(version>=1.6)(!(version>=2.0.0))))"

Of course, there is also a generalized form of the Export-Package header that describes the features provided by a bundle. The Require-Capability header can be used to express various requirements. The header

Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.8))"

states that the component requires JavaSE 8 as runtime environment. As you can have a header only once in the MANIFEST.MF, all requirements of a bundle are combined in one header, using a comma as separator. An article in the OSGI Alliance Block gives a short introduction to the general “Requirements and Capabilities” model.

This powerful model allows you to easily choose a component providing required capabilities from a repository if—and that’s the important point in this context—you can query the repository for such a component, using the “Require-Capability” expression as search expression. This is the main functionality of the OSGi repository interface. Pity enough, this kind of query is something that Maven Repositories in general don’t support.

OSGi “native” repositories

In its simplest form, the persistent data of a “native” OSGi Bundle Repository (OBR) consists of a directory with bundles and an index file that describes the capabilities (and requirements) of the bundles in the repository. The information in the index file is extracted from the manifests of the bundles in the repository.

If you have followed this tutorial step-by-step, the cnf directory of your sample bnd workspace contains three such repositories: local, release and OSGi-Getting-Started. They are made known to bnd in cnf/build.bnd by these statements:

-plugin.1.Local: \
	aQute.bnd.deployer.repository.LocalIndexedRepo; \
		name=Local; \
		local=${workspace}/cnf/localrepo; \
		pretty=true

-plugin.2.Release: \
	aQute.bnd.deployer.repository.LocalIndexedRepo; \
		name=Release; \
		local=${workspace}/cnf/release; \
		pretty=true

-plugin.3.OSGIGettingStarted: \
	aQute.bnd.repository.osgi.OSGiRepository;\
		name="OSGi-Getting-Started";\
		locations=https://raw.githubusercontent.com/mnlipp/osgi-getting-started/master/cnf/indexed-maven/index.xml

The local and release repositories have been created by the workspace template and are empty. Their plugin type aQute.bnd.deployer.repository.LocalIndexedRepo provides an editable repository in the local file system. “Editable” means that you can deploy bundles to the repository and the index is regenerated automatically by bndtools. Details about the configuration properties can be found in the bndtools documentation.

Another implementation of an OSGi repository is the aQute.bnd.deployer.repository.osgi.OSGiRepository. This type of repository uses the standardized index file format and is “fixed”, i. e. bndtools provides no functions to modify the content of the repository. The index file is specified by a URL, which usually references data on a remote server. Again, the configuration options can be found in the bndtools documentation.

The configuration for the “OSGI-Getting-Started” repository above references an index file that is maintained together with the sample projects. The repository described by the index file contains all bundles required for the examples (and some more) in this introduction.

In an ideal world (from OSGi’s point of view) all freely available bundles would be maintained in a “native” repository. Not necessarily using the simple persistence, because this could result in a very large index file. Rather, a server would provide some remotely accessible implementation of the Repository API. In reality, however, there have never been more than a few “vendor” maintained repositories (such as the “Felix” repository2)3 and some special purpose repositories maintained by individual projects (such as the “Bndtools Hub”).

OSGi views on Maven repositories

Today’s Java development is Maven Central centric4. No matter whether a project uses Maven or some other tool like Gradle for project management, (almost) everybody makes Java Open Source Software artifacts available on Maven Central and downloads required libraries from there using their Maven coordinates. As mentioned before, the deficiency from an OSGi’s point of view is that Maven repositories do not provide the requirements and capabilities based search facility5.

A nice solution would be to have an OSGi search facility maintained by e.g. the OSGi Alliance that uses the major public repositories as backing repositories6. Since no such facility exists, bnd workspaces have to fall back to downloading a subset of a Maven repository’s artifacts and creating an OSGi Bundle Repository (index) from them. Various bnd plugins exist for that purpose.

Maven Bnd Repository Plugin

The aQute.bnd.repository.maven.provider.MavenBndRepository is probably the easiest to setup. Here is a sample configuration (details can be found in the bnd documentation):

-plugin.CentralMvn: \
	aQute.bnd.repository.maven.provider.MavenBndRepository; \
		name="Central (Maven)"; \
		snapshotUrl=https://oss.sonatype.org/content/repositories/snapshots/; \
		releaseUrl=https://repo.maven.apache.org/maven2/; \
		index=${.}/central.mvn

The plugin downloads and includes into the repository the artifacts specified in the file configured with the index property7. The artifacts are specified as a list of maven coordinates, one per line. If you have the Repositories view open in Eclipse (usually when you are in the bndtools perspective), you can even drag and drop the URL of a POM file (e.g. found by searching Maven Central) into a Maven Bnd Repository and the Maven coordinates will be added to the index file.

This sounds like a nice way to get the artifacts that your project depends on, until you notice that the plugins don’t support transitive dependencies. You get exactly what you have specified, and if an artifact depends on some other artifact, you have to explicitly specify this as well – a very arduous task.

Bnd POM Repository Plugin

This is where the aQute.bnd.repository.maven.pom.provider.BndPomRepository plugin comes in (details can, again, be found in the bnd documentation). This plugin is kind of a strange beast, because it allows you to configure an initial set of artifacts to retrieve in very different ways. No matter how you obtain the initial set of artifacts, the plugin’s default behavior is to also include the transitive dependencies, i.e. the dependencies specified in the POM files of the artifacts.

The preferred usage seems to be to maintain a pom.xml with all (direct) dependencies8 of your project. This works, but it means that you have to maintain all these dependencies in this file in addition to your build configuration – again a quite arduous task. And it doesn’t allow you to use the repositories view to browse for newer versions of an artifact because only the versions specified explicitly are included into the repository.

Using the search feature seems more promising. As an example, we should be able to get all Apache Felix artifacts and the artifacts that they depend on with a Bnd POM Repository configured like this:

-plugin.Felix: \
    aQute.bnd.repository.maven.pom.provider.BndPomRepository; \
        name=Felix; \
    	snapshotUrls=https://oss.sonatype.org/content/repositories/snapshots/; \
        releaseUrls=https://repo1.maven.org/maven2; \
        query='q=g:%22org.apache.felix%22&rows=10000'

Here’s a similar query for Equinox. Note that they have changed the group id in later versions, so in order to get everything, we have to query using the artifact id.

-plugin.Equinox = \
    aQute.bnd.repository.maven.pom.provider.BndPomRepository; \
        name=Equinox; \
    	snapshotUrls=https://oss.sonatype.org/content/repositories/snapshots/; \
        releaseUrls=https://central.maven.org/maven2; \
        query='q=a:%22org.eclipse.osgi%22&rows=10000'

I have personally experienced two problems with the search feature of this plugin. The first is that the search works only with Maven Central, which means that it won’t include snapshots of artifacts.

The second problem (I found out about that at the beginning of 2022) is that maven central doesn’t support a rows parameter greater 200 in the query any more (and the plugin doesn’t support pagination). So for the example queries above it has become useless.

Indexed Maven Repository Plugin

My Indexed Maven Repository Plugin maintains an index of one or more maven repositories using a configuration that is aligned with maven group ids. It makes use of the maven dependency information both for adding artifacts to the index and for filtering artifacts. For more details, have a look at the plugin’s documentation.

This plugin is used in the workspace with the examples used in this introductions. The OBR index that it produces as a side effect is the one that you have used in your workspace for the “OSGi-Getting-Started” repository, if you have followed the introduction step-by-step.

Nexus Search Repository Plugin

In a previous attempt to index maven artifacts, I had started a Nexus Search Plugin plugin that—as its name suggests—uses the Nexus search API to find both the released artifacts and the snapshot artifacts on a Nexus server (especially those on the Open Source repository).

I’m not perfectly sure yet, but with the Indexed Maven Repository Plugin available, I consider to deprecate this plugin.

Aether Plugin

The bndtools documentation also mentions an “Aether (Maven) Repositories” plugin that I haven’t tested. The documentation seems incomplete and what I could find in addition was not encouraging. Besides, the underlying Eclipse Aether project has been archived and isn’t easily accessible any more.

JPM Repository Plugin (deprecated)

When you create a new workspace with bndtools 3.5.0, it configures an aQute.bnd.jpm.Repository plugin for accessing Maven Central:

# Configure Repositories
-plugin.1.Central: \
	aQute.bnd.deployer.repository.wrapper.Plugin; \
		location = "${build}/cache/wrapper"; \
		reindex = true, \
	aQute.bnd.jpm.Repository; \
		includeStaged = true; \
		name = Central; \
		location = ~/.bnd/shacache; \
		index = ${build}/central.json

This initial setup is a bit strange, considering the fact that JPM support will be removed from bndtools in the next release. Details about the configuration of the plugin could be found in OSGi’s enroute Maven tutorial.

Conclusion

Once you have found a configuration that suits your needs, it isn’t too hard to include content from the popular Maven repositories in your bnd/bndtools projects. A different issue is how you make the bundles from your own project available to others. This is closely related to your project’s build and CI/CD chain. I’ll cover this topic in another part.


  1. In more general terms, we’re talking about contracts between components. 

  2. Until 2020 you could make use of an OBR repository to access the Apache Felix bundles like this:

    -plugin.5.Felix: \
        aQute.bnd.deployer.repository.osgi.OSGiRepository; \
        name=Felix; \
        locations=https://felix.apache.org/obr/releases.xml
    

  3. Sometimes badly maintained, as you can see here and here

  4. Sorry, couldn’t resist. 

  5. At least not out-of-the-box. I found that Sonatype’s Nexus repository manager9 can provide a Virtual OSGi Bundle Repository for a Maven repository. However, this does not seem to be enabled on their most popular installation. And it doesn’t look as if it is planned to support it in version 3 any more. 

  6. The “JPM” project (Java Package Manager) developed by Peter Kriens when he was not working for the OSGi Alliance included an attempt to create a “Meta Repository”10 with the possibility to query bundles by packages. You will still find references to it when searching for sample OSGi project configurations in the Web, but the JPM Web site was “officially” declared down on March 1st, 2017. Support for JPM will be removed from bnd in the next release

  7. Not to be confused with an OBR index file. 

  8. I found the example in the documentation to be incomplete. You need a full fledged pom.xml such as:

    <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>local</groupId>
        <artifactId>central</artifactId>
        <version>0.0.0</version>
        <packaging>pom</packaging>
        <dependencies>
            <dependency>
                <groupId>...</groupId>
                <artifactId>...</artifactId>
                <version>...</version>
            </dependency>
        </dependencies>
    </project>
    

  9. I have no relations with this company except for an account on oss.sonatype.org. It just happens that I found the feature description when searching through the web. If you’re aware of other repository products with OSGi support, tell me and I’ll add it here. 

  10. Actually, the project had a somewhat broader scope. The idea was to provide a jpm command that would work similar to the npm. From the (now gone) Web Site:

    jpm4j is a package manager for java. Languages like Ruby with its Gems, Node.js with npm, and Perl’s CPAN, provide a package manager that makes it easy to install libraries and applications regardless of platform. jpm4j unleashes the power of Java with a “write once, deploy anywhere” model. Just publish the binaries on the Internet, notify jpm4j, and then deploy from anywhere to any platform with jpm4j.

    An open index provides already organized access to almost binaries (and growing) organized in programs. An index that is searchable, editable, rankable, and extendable (in real time) by you. An index that can be used during development from a growing number of build technologies and IDEs (including of course Eclipse and Maven).

    (Retrieved from WayBackMachine