3.3. Automatic Import Versioning
Overview
By default, the Maven bundle plug-in automatically assigns a version range to imported packages, following the semantic versioning rules outlined in Section 3.1, “Semantic Versioning”. You need to provide an additional hint to the bundle plug-in, to indicate whether the bundle imports a package in the role of a consumer or a provider (the bundle plug-in presumes the consumer role).
For automatic import versioning to work, the package dependency must be versioned at build time, otherwise the importing bundle cannot calculate the import range.
Automatic consumer import range
The Maven bundle plug-in automatically generates consumer import ranges that conform to the rules of semantic versioning, as defined in the section called “Consumer import range”. The only prerequisite is that the corresponding exporter actually defines a package version.
For example, given that the
hello-paris
bundle consumes version 1.2.1
of the org.fusesource.example.time
package, the Maven bundle plug-in automatically generates a manifest with the following import:
Import-Package: org.fusesource.example.time;version="[1.0,2)"
Automatic provider import range
Because the Maven bundle plug-in assumes by default that an importer is acting in the role of consumer, it is necessary to specify explicitly when an importer is acting in the role of provider, using the
provide:=true
clause. There are two different approaches you can take to specifying the provider import range, depending on how you package the API and provider bundles, as follows:
Separate API and provider bundles
When the API and the provider are to be packaged in separate bundles, append the
provide:=true
clause to the relevant API packages listed in the Import-Package
instruction.
For example, given that the
hello-paris-impl
bundle provides the implementation of the org.fusesource.example.hello.paris
package from the hello-paris
API bundle, you would define the Import-Package
instructions for the hello-paris-impl
bundle as follows:
<instructions>
...
<Import-Package>
${project.groupId}.hello.paris*;provide:=true,
*
</Import-Package>
...
</instructions>
Given that the
org.fusesource.example.hello.paris
package has version 1.0
, the Maven bundle plug-in generates a manifest with the following imports:
Import-Package: org.fusesource.example.hello.paris;version="[1.0,1.1)"
,org.fusesource.example.time;version="[1.0,2)",org.osgi.service.bluep
rint;version="[1.0.0,2.0.0)"
Combined API and provider bundle
When the API and the provider are to be combined in the same bundle, append the
provide:=true
clause to the relevant API packages listed in the Export-Package
instruction (this is the correct setting to use both for an API/provider combination bundle and for an API/provider build-time combination bundle).
For example, given that the
hello-boston
bundle includes both the org.fusesource.example.hello.boston
API and its implementation classes, you would define the Export-Package
instructions for the hello-boston
bundle as follows:
<instructions>
...
<Export-Package>
!${project.groupId}*.impl.*,
!${project.groupId}*.internal.*,
${project.groupId}.hello.boston*;provide:=true;version=${project.version}
</Export-Package>
...
</instructions>
Given that the
org.fusesource.example.hello.boston
package has version 1.0
, the Maven bundle plug-in generates a manifest with the following highlighted import and export:
Import-Package: org.fusesource.example.hello.boston;version="[1.0,1.1) ",org.fusesource.example.time;version="[1.0,2)",org.osgi.service.blue print;version="[1.0.0,2.0.0)" Export-Package: org.fusesource.example.hello.boston;uses:="org.fusesou rce.example.time";version="1.0"
Note
In the case where the API and the provider code are located in separate Maven projects, setting
provide:=true
on an exported API package in the provider's POM has the important side-effect that the API interfaces are included in the provider bundle. For a detailed explanation, see Section 2.7, “API/Provider Build-Time Combination”.
Customize import range
Occasionally, it will be necessary to customize the import version range—for example, if the corresponding exporter does not follow the OSGi semantic versioning conventions. The simplest case is where you specify the import range explicitly, as in the following example:
<Import-Package> com.package.with.wrong.semantics*;version="[1.3,1.3.4]", * </Import-Package>
Custom import rule
Sometimes, a third-party exporter might follow a consistent versioning convention, but this convention is different from the OSGi convention. In this case, it make sense to define a custom import rule that codifies the alternative convention. For example, if a third-party exporter increments the minor version number whenever binary compatibility with consumers is broken, you could use Bnd's
range
macro to codify this rule on consumer imports, as follows:
<Import-Package><![CDATA[ com.package.with.wrong.semantics*;version="$<range;[==,=+)>", * ]]> </Import-Package>
Where the Bnd macro is written in the format,
$<Macro>
and must be enclosed in a CDATA section to avoid clashing with the XML interpretations of <
and >
(actually, Bnd supports a variety of different macro delimiters, most of which cannot be used here: braces, ${...}
, clash with Maven properties; while square brackets, $[...]
, and parentheses, $(...)
, clash with the syntax of version ranges).
Entries like
==
and =+
are masks that return a version based on the version of the corresponding exporter. The equals sign, =
, returns the corresponding version part unchanged; the plus sign, +
, returns the corresponding version part plus one; and the minus sign, -
, returns the corresponding version part minus one. For example, if the corresponding exporter has the version, 1.3.0.4
, the range mask, [==,=+)
, resolves to [1.3,1.4)
. For more details, consult the Bnd documentation for the range macro.
The
range
macro is a relatively new addition to Bnd. In POMs that were written before the range
macro became available, you might come across ranges written using the Bnd version
macro. For example, the range, $<range;[==,=+)>
, could also be written using the version
macro as follows:
<Import-Package><![CDATA[ com.package.with.wrong.semantics*;version="[$<version;==>,$<version;=+>)", * ]]> </Import-Package>
Note
At the time of writing, the Bnd tool has a bug that causes two
NullPointerException
exceptions, accompanied by lengthy stack traces, to be generated whenever you build a Maven project featuring the range
or version
macros as shown above. Although alarming, these exceptions are harmless non-fatal errors. If you check the generated bundle after building, you will see that the Manifest.mf
file is generated exactly as specified by the bundle instructions.
Customize version policies
Bnd and the Maven bundle plug-in support directives that enable you to override the default versioning policies. Occasionally, you might come across an entire subsystem that consistently follows a different versioning convention from OSGi and, in this case, it makes sense to override the default versioning policies.
Since Bnd version 1.15 (version 2.2.0 of the Maven bundle plug-in), the following directives can be used to specify versioning policies:
-consumer-policy
- Specifies a macro to calculate the consumer import range.
-provider-policy
- Specifies a macro to calculate the provider import range.
In the Maven bundle plug-in, these directives are set using the elements,
_consumer-policy
and _provider-policy
—for example:
<instructions> ... <_consumer-policy>$<range;[==,=+)></_consumer-policy> <_provider-policy>$<range;[===,==+)></_provider-policy> ... </instructions>
Where the macro,
$<Macro>
, is escaped as, $<Macro>
, in XML (or you could use a CDATA section).
Note
Any Bnd directives that start with the hyphen character,
-
, can also be set in the Maven bundle plug-in using an element whose name is obtained by changing the initial hyphen, -
, to an underscore, _
.