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.
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”).
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.
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.
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.
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.
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.
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.
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.
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.
In more general terms, we’re talking about contracts between components. ↩
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
Sorry, couldn’t resist. ↩
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. ↩
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. ↩
Not to be confused with an OBR index file. ↩
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>
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. ↩
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) ↩