Red Hat Training

A Red Hat training course is available for Red Hat Fuse

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>$&lt;range;[==,=+)></_consumer-policy>
  <_provider-policy>$&lt;range;[===,==+)></_provider-policy>
  ...
</instructions>
Where the macro, $<Macro>, is escaped as, $&lt;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, _.