Fuse 6 is no longer supported
As of February 2025, Red Hat Fuse 6 is no longer supported. If you are using Fuse 6, please upgrade to Red Hat build of Apache Camel.Managing OSGi Dependencies
How to package applications for OSGi containers
Copyright © 2011-2020 Red Hat, Inc. and/or its affiliates.
Abstract
Chapter 1. Bundle Class Loader Copy linkLink copied to clipboard!
Abstract
1.1. Class Loader Basics Copy linkLink copied to clipboard!
Overview Copy linkLink copied to clipboard!
Bundle class loader Copy linkLink copied to clipboard!
- A class loader encapsulates knowledge about where to find classes and a particular bundle might have knowledge about finding classes that other bundles do not possess. For example, a bundle can have private classes, not directly visible to other bundles.
- In order to support the flexible OSGi class loading architecture, each bundle needs to be in control of how and where it finds its classes.
Identity of a loaded class Copy linkLink copied to clipboard!
org.foo.Hello—is not sufficient to identify a loaded class uniquely. In general, a loaded class is uniquely identified by combining the classloader identity with the fully-qualified class name.
org.foo.Hello, gets loaded by the bundle classloader for version 1.3 of bundle A, the loaded class is uniquely identified by the combination of the class loader, A;1.3, and the class name, org.foo.Hello. In this chapter, the following notation is used for the unique identity of the loaded class:
A;1.3/org.foo.Hello
A;1.3/org.foo.Hello
Bundle class space Copy linkLink copied to clipboard!
Multiple copies of a loaded class Copy linkLink copied to clipboard!
org.foo.Hello. In this case, you would have two distinct loaded classes, as follows:
A;1.3/org.foo.Hello B;2.0/org.foo.Hello
A;1.3/org.foo.Hello
B;2.0/org.foo.Hello
Class loader delegation Copy linkLink copied to clipboard!
- Initial class loader
- The class loader that is initially asked to load the class.
- Effective class loader
- The class loader that actually loads the class, after delegation is taken into account.
1.2. Conflicting Classes Copy linkLink copied to clipboard!
Overview Copy linkLink copied to clipboard!
Symbolic references Copy linkLink copied to clipboard!
TestHello class, which is loaded by the class loader, A.
Example 1.1. Symbolic Reference Used in a Class Cast
org.foo.Hello, is initially loaded by class loader A, this does not imply that the symbolic references must be resolved to A/org.foo.Hello. If the initial class loader A decides to delegate to another class loader, B, the symbolic reference could be resolved to B/org.foo.Hello instead (so that B is the effective class loader). The delegation mechanism is crucial, because it enables an OSGi bundle to re-use existing loaded classes and avoid class space inconsistencies.
Class cast exceptions Copy linkLink copied to clipboard!
TestHello class shown in Example 1.1, “Symbolic Reference Used in a Class Cast”, where the org.foo.Hello symbolic reference has been resolved to A/org.foo.Hello. If you also have a Hello object that is an instance of B/org.foo.Hello type, you will get a class cast exception when you pass this object to the TestHello.useHelloObj(Object) method. Specifically, in the line of code that performs the following cast:
org.foo.Hello h = (org.foo.Hello) hello;
org.foo.Hello h = (org.foo.Hello) hello;
org.foo.Hello symbolic reference has been resolved to the A/org.foo.Hello type, but the hello object is of B/org.foo.Hello type. Because the types do not match, you get the class cast exception.
1.3. Class Loading Algorithm Copy linkLink copied to clipboard!
Overview Copy linkLink copied to clipboard!
Requirements on a bundle class loader Copy linkLink copied to clipboard!
- In order to avoid loading multiple copies of a class, the bundle class loader must first of all try to find the class in one of its dependent bundles.
- The bundle class loader must never load a duplicate copy of a class into its class space.
Bundle class loading algorithm Copy linkLink copied to clipboard!
- If the class belongs to one of the
java.*packages or any packages listed in theorg.osgi.framework.bootdelegationproperty, the bundle class loader delegates to the parent class loader. - If the class belongs to one of the packages listed in
Import-Package, the bundle class loader delegates loading to the corresponding exporter bundle. - If the class belongs to one of the packages imported by
Require-Bundle, the bundle class loader delegates loading to the corresponding exporter bundle.NoteIt is strongly recommended that you avoid using theRequire-Bundleheader. OSGi dependencies are meant to be defined at package granularity, not bundle granularity. - Next, the bundle class loader looks for the class amongst its internal classes (inside its own JAR file).
- Next, the bundle class loader searches the internal classes of any fragments attached to the bundle.
- Finally, if the class belongs to one of the packages imported using
DynamicImport-Package, the bundle class loader delegates loading to the corresponding exporter bundle (if there is one).
Chapter 2. Importing and Exporting Packages Copy linkLink copied to clipboard!
Abstract
2.1. Overview of Bundle Types Copy linkLink copied to clipboard!
Overview Copy linkLink copied to clipboard!
Figure 2.1. Relationship between API, Provider, and Consumer Bundles
Bundle Types Copy linkLink copied to clipboard!
- Library bundle
- A library bundle contains Java classes and interfaces, which are public and intended to be used by other bundles. Often, in a library, there is no formal separation between API interfaces and implementation classes. Instead, developers simply instantiate and use the classes provided by the library.A library bundle does not publish or access any OSGi services.
- API bundle
- A pure API bundle contains only Java interfaces (or abstract classes), which are public. The implementation of the API is meant to be provided in a separate bundle or JAR.An API bundle does not publish or access any OSGi services.
- Provider bundle
- A provider bundle contains the classes that implement an API. Usually, the classes in a provider bundle are all private and are not exported from the bundle.The natural mechanism for a provider bundle to expose its functionality is to create and publish one or more OSGi services (where the OSGi services are then accessed through the public API).
- Consumer bundle
- A consumer bundle contains code that uses an API.A consumer bundle typically accesses one or more OSGi services; it does not usually publish an OSGi service itself (unless it is acting as a hybrid of a consumer and a provider).
- API/provider combination bundle
- In some cases, it can make sense to package API packages and implementation packages together, resulting in an API/provider combination bundle. For example, if you intend to provide only one implementation of an API, it can be simpler to package the API and its implementation in a single bundle.For the API/provider combination bundle, it is assumed that the API code and the implementation code belong to the same Maven project.
- API/provider build-time combination bundle
- Even if you opt for a formal separation of API code and implementation code—that is, where the API code and the implementation code are developed in separate Maven projects—you might nevertheless want to add the API interfaces to the provider bundle. Peter Kriens (creator of the Bnd tool, on which the Maven bundle plug-in is based) recommends that a provider bundle should always include its API interfaces. One reason for this is that, when you deploy a provider bundle without embedding the API, there always a risk that the required version of the API will not be available in the OSGi container at deploy time. By embedding the API interfaces in the provider, you eliminate that risk and improve the reliability of your deployment.When you develop your API interfaces and your implementation classes in separate Maven projects, the bundle plug-in supports a special configuration that enables you to embed the API in the provider bundle at build-time. Hence, this bundle is called an API/provider build-time combination.
Summary of import/export rules Copy linkLink copied to clipboard!
| Bundle Type | Export Own Packages | Import Own Packages | Publish OSGi Service | Access OSGi Service |
|---|---|---|---|---|
| Library | Yes | |||
| API | Yes | |||
| Provider | Yes | |||
| Consumer | Maybe | Maybe | Yes | |
| API/provider combination | Yes (API only) | Yes (API only) | Yes |
Importing and exporting own packages Copy linkLink copied to clipboard!
When to publish an OSGi service Copy linkLink copied to clipboard!
bean element to create an instance of an implementation class.
Default bundle plug-in settings Copy linkLink copied to clipboard!
Example 2.1. Maven Bundle Plug-In Settings
- Import rules—when you build your Maven project, the bundle plug-in tries to discover all of the bundle's package dependencies automatically. By default, the bundle plug-in then adds all of the discovered package dependencies to the
Import-Packageheader in the Manifest file (equivalent to the wildcard instruction,*).However, the effectiveness of the initial scan depends on where the dependencies actually arise, as follows:- Java source code—Bnd scans the Java source code in your project to discover package dependencies. There is one blind spot in this process, however: the scan cannot detect dependencies that arise from using Java reflection and explicit calls on a class loader.
- Spring configuration file—Bnd is not capable of scanning Spring configuration files to discover package dependencies. Any dependencies arising from Spring configuration must be added manually to the
Import-Packageinstruction. - Blueprint configuration file—the Apache Karaf implementation of blueprint is capable of mapping XML namespaces to Java packages and dynamically resolving the resulting dependencies at run time. Hence, there is no need for blueprint's dependencies to be added to the
Import-Packageinstruction.This is one of the major benefits of using blueprint configuration.
Provided that the dependent bundles define a version for their exported packages, the bundle plug-in also automatically assigns a version range to the imported packages (see Section 3.3, “Automatic Import Versioning”). - Export rules—by default, the Maven bundle plug-in exports all of the packages in your project, except for packages that match the patterns
*.implor*.internal. It is therefore recommended that you follow the convention of naming all of your private packages with the suffix*.implor*.internal.By default, the bundle plug-in does not assign a version to the exported packages. This is a major disadvantage of the default export instruction (see Section 3.2, “Export Versioning”).
2.2. Sample OSGi Application Copy linkLink copied to clipboard!
Example of bundle dependencies Copy linkLink copied to clipboard!
hello-consumer bundle, which imports a HelloParis service from the hello-paris bundle and imports a HelloBoston service from the hello-boston bundle.
Figure 2.2. Sample OSGi Application
The sample bundles Copy linkLink copied to clipboard!
time-util- Fits the pattern of a library bundle. The
time-utilbundle is a utility library that can createClockinstances that tell the time in a particular time zone.Thetime-utilbundle is implemented using classes from the JDK and thus has no external package dependencies. hello-paris- Fits the pattern of an API bundle. The
hello-parisbundle consists of a single Java interface, which returns a greeting,getGreeting(), and gives the local time in Paris,getLocalTime().Thehello-parisbundle has the following external package dependency:org.fusesource.example.time
org.fusesource.example.timeCopy to Clipboard Copied! Toggle word wrap Toggle overflow hello-paris-impl- Fits the pattern of a provider bundle. The
hello-paris-implbundle implements thehello-parisAPI bundle.Thehello-paris-implbundle has the following external package dependencies:org.fusesource.example.hello.paris org.fusesource.example.time
org.fusesource.example.hello.paris org.fusesource.example.timeCopy to Clipboard Copied! Toggle word wrap Toggle overflow hello-boston- Fits the pattern of an API/provider combination bundle. The
hello-bostonbundle combines a Java interface and its implementation, where the Java interface returns a greeting,getGreeting(), and gives the local time in Boston,getLocalTime().Thehello-bostonbundle has the following external package dependency:org.fusesource.example.time
org.fusesource.example.timeCopy to Clipboard Copied! Toggle word wrap Toggle overflow hello-consumer- Fits the pattern of a consumer bundle. The
hello-consumerbundle imports theHelloParisOSGi service and theHelloBostonOSGi service and then invokes on these services to report the local times in Paris and in Boston.Thehello-consumerbundle has the following external package dependencies:org.fusesource.example.hello.paris org.fusesource.example.hello.boston org.fusesource.example.time
org.fusesource.example.hello.paris org.fusesource.example.hello.boston org.fusesource.example.timeCopy to Clipboard Copied! Toggle word wrap Toggle overflow
2.3. Library Bundle Copy linkLink copied to clipboard!
Overview Copy linkLink copied to clipboard!
time-util bundle exemplifies a library bundle, where the main purpose of a library is to make interfaces and classes available to other bundles. Hence, the library should export all of its own packages and associate a version number with the exported packages.
Directory structure Copy linkLink copied to clipboard!
time-util bundle has the following directory structure:
src/main/java sub-directory. The org.fusesource.example.time package is public and all of its classes and interfaces can be exported from the bundle.
Sample implementation Copy linkLink copied to clipboard!
time-util bundle is essentially a wrapper around some of the standard time utilities in Java. It provides a Clock class, which returns the local time in a particular time zone when you invoke the Clock.getLocalTime() method. The Clock class is defined as follows:
TimeUtil class is a factory that is used to create Clock instances for particular time zones. Two time zones are supported: TimeZone.BOSTON and TimeZone.PARIS. The TimeUtil class is defined as follows:
Import and export rules Copy linkLink copied to clipboard!
time-util bundle:
- Exporting own packages—the
org.fusesource.example.timepackage is public, and must be exported. - Importing own packages—none of the bundle's own packages should be imported, which is the usual case for a library bundle.
- Importing dependent packages—any external package dependencies must be imported. In this particular example, however, there are none (the
time-utilbundle depends only on classes from the JVM).
Maven bundle plug-in settings Copy linkLink copied to clipboard!
org.fusesource.example.time (coded as ${project.groupId}.time). The Export-Package instruction also contains entries to block the export of any packages containing .impl or .internal. In this case, the bundle plug-in instructions are as follows:
Generated MANIFEST.MF file Copy linkLink copied to clipboard!
MANIFEST.MF file:
2.4. API Bundle Copy linkLink copied to clipboard!
Overview Copy linkLink copied to clipboard!
hello-paris bundle exemplifies an API bundle, which contains only public Java interfaces. Hence, the API bundle should export all of its own packages and associate a version number with the exported packages.
Directory structure Copy linkLink copied to clipboard!
hello-paris bundle has the following directory structure:
src/main/java sub-directory. The org.fusesource.example.hello.paris package is public and all of its classes and interfaces can be exported from the bundle.
Sample API Copy linkLink copied to clipboard!
hello-paris bundle is a pure API, which means it contains only Java interfaces. In this example, there is a single interface, HelloParis, which returns a localized greeting and a Clock object that tells the local time. The HelloParis interface is defined as follows:
Maven dependencies Copy linkLink copied to clipboard!
hello-paris bundle defines dependencies on the following Maven artifact:
time-util
Import and export rules Copy linkLink copied to clipboard!
hello-paris bundle:
- Exporting own packages—the
org.fusesource.example.hello.parispackage is public, and must be exported. - Importing own packages—none of the bundle's own packages should be imported.
- Importing dependent packages—any external package dependencies must be imported.
Maven bundle plug-in settings Copy linkLink copied to clipboard!
org.fusesource.example.hello.paris (coded as ${project.groupId}.hello.paris*). The Export-Package instruction also contains entries to block the export of any packages containing .impl or .internal. In this case, the bundle plug-in instructions are as follows:
Generated MANIFEST.MF file Copy linkLink copied to clipboard!
MANIFEST.MF file:
Import-Package header lists one external package dependency, org.fusesource.example.time. None of the bundle's own packages are imported.
Export-Package header is used to export the API package, org.fusesource.example.hello.paris, while the uses clause declares a transitive dependency on the org.fusesource.example.time package.
2.5. Provider Bundle Copy linkLink copied to clipboard!
Overview Copy linkLink copied to clipboard!
hello-paris-impl bundle exemplifies a provider bundle, which provides the implementation of an API. The provider does not export any of its own packages, because the implementation classes are private. But the provider does instantiate and export an OSGi service, which is accessed through the HelloParis interface.
Directory structure Copy linkLink copied to clipboard!
hello-paris-impl bundle has the following directory structure:
org.fusesource.example.hello.paris.impl package is private. By default, the Maven bundle plug-in treats any packages containing the segments .impl or .internal, as private packages.
src/main/resources/OSGI-INF/blueprint directory contains a single blueprint file, paris-svc.xml. Any file matching the pattern, *.xml, in this directory is assumed to be a blueprint configuration file.
Sample implementation Copy linkLink copied to clipboard!
hello-paris-impl bundle is intended to implement all of the interfaces appearing in the hello-paris API bundle. In this example, a single HelloParisImpl class implements the HelloParis interface, as follows:
Publish OSGi service Copy linkLink copied to clipboard!
HelloParis implementation in OSGi is to publish the class, HelloParisImpl, as an OSGi service. Use the bean element to create a HelloParisImpl instance and use the service element to publish the bean, advertising it as a service of HelloParis type.
OSGI-INF/blueprint/paris-svc.xml, has the following contents:
Maven dependencies Copy linkLink copied to clipboard!
hello-paris-impl bundle defines dependencies on the following Maven artifacts:
time-utilhello-paris
Import and export rules Copy linkLink copied to clipboard!
hello-paris-impl bundle:
- Exporting own packages—none of the bundle's own packages should be exported; they are all private.
- Importing own packages—none of the bundle's own packages should be imported.
- Importing dependent packages—any external package dependencies must be imported.
Maven bundle plug-in settings Copy linkLink copied to clipboard!
Export-Package instruction), as long as you take care to put all of your code into packages containing .impl or .internal (which are not exported by default). You should explicitly list the implemented API, org.fusesource.example.hello.paris, in the Import-Package instruction and add the provide:=true clause to it. This signals that this bundle is acting as the provider of the hello.paris package (and ensures that the API is imported with the correct version range—see Section 3.3, “Automatic Import Versioning”). In this case, the bundle plug-in instructions are as follows:
Generated MANIFEST.MF file Copy linkLink copied to clipboard!
MANIFEST.MF file:
Import-Package header imports external package dependencies only—for example, org.fusesource.example.hello.paris and org.fusesource.example.time.
Export-Service header advertises the OSGi service as a HelloParis instance. This enables clients to find the HelloParisImpl instance by searching for a service of HelloParis type.
2.6. API/Provider Combination Copy linkLink copied to clipboard!
Overview Copy linkLink copied to clipboard!
hello-boston bundle exemplifies an API/provider combination bundle. The API/provider bundle exports only its API packages, while the implementation packages are kept private. The API/provider bundle also instantiates and exports an OSGi service, which is accessed through the HelloBoston interface.
Directory structure Copy linkLink copied to clipboard!
hello-boston bundle has the following directory structure:
org.fusesource.example.hello.boston package is public and all of its classes and interfaces are exported from the bundle.
org.fusesource.example.hello.boston.impl package is private. By default, the Maven bundle plug-in treats any packages containing the segments .impl or .internal, as private packages.
src/main/resources/OSGI-INF/blueprint directory contains a single blueprint file, boston-svc.xml. Any file matching the pattern, *.xml, in this directory is assumed to be a blueprint configuration file.
Sample API Copy linkLink copied to clipboard!
hello-boston bundle contains an API, which consists of the interface, HelloBoston. The HelloBoston interface returns a localized greeting and a Clock object that tells the local time. It is defined as follows:
Sample implementation Copy linkLink copied to clipboard!
hello-boston bundle also implements the API. In this example, a single HelloBostonImpl class implements the HelloBoston interface, as follows:
Publish OSGi service Copy linkLink copied to clipboard!
HelloBoston implementation in OSGi is to publish the class, HelloBostonImpl, as an OSGi service. Use the bean element to create a HelloBostonImpl instance and use the service element to publish the bean, advertising it as a service of HelloBoston type.
OSGI-INF/blueprint/boston-svc.xml, has the following contents:
Maven dependencies Copy linkLink copied to clipboard!
hello-boston bundle defines dependencies on the following Maven artifact:
time-util
Import and export rules Copy linkLink copied to clipboard!
hello-boston bundle:
- Exporting own packages—export the public API package,
org.fusesource.example.hello.boston. Do not export the private package,org.fusesource.example.hello.boston.impl. - Importing own packages—import the public API package,
org.fusesource.example.hello.boston. - Importing dependent packages—any external package dependencies must be imported.
Maven bundle plug-in settings Copy linkLink copied to clipboard!
org.fusesource.example.hello.boston, by including it in the Export-Package instruction, and add the provide:=true clause to it. This signals that this bundle is acting as the provider of the hello.boston package (and ensures that the API is imported with the correct version range—see Section 3.3, “Automatic Import Versioning”). In this case, the bundle plug-in instructions are as follows:
Example 2.2. Plug-In Settings for API/Provider Combination
Generated MANIFEST.MF file Copy linkLink copied to clipboard!
MANIFEST.MF file:
Import-Package header imports both the public API package, org.fusesource.example.hello.boston, and the external package dependencies—for example, org.fusesource.example.time.
Export-Package header exports the public API package, org.fusesource.example.hello.boston, while the uses clause declares a transitive dependency on the org.fusesource.example.time package.
Export-Service header advertises the OSGi service as a HelloBoston instance. This enables clients to find the HelloBostonImpl instance by searching for a service of HelloBoston type.
2.7. API/Provider Build-Time Combination Copy linkLink copied to clipboard!
Overview Copy linkLink copied to clipboard!
hello-paris-impl bundle. By exploiting the Bnd utility's provide:=true clause, you can modify the instructions for the Maven bundle plug-in, so that the org.fusesource.example.hello.paris API package gets included in the hello-paris-impl bundle at build time.
provide:=true clause Copy linkLink copied to clipboard!
provide:=true clause to the API package, either in an import instruction or in an export instruction (otherwise, by default, the bundle plug-in would assume that the bundle is a consumer of the API package). In particular, for an API/provider build-time combination bundle, you must export the org.fusesource.example.hello.paris API package and attach the provide:=true clause, as follows:
provide:=true clause has a very important side effect: if the code for the exported package is not in the current Maven project, the Maven bundle plug-in adds the package code to the bundle at build time.
Included Maven projects Copy linkLink copied to clipboard!
hello-paris-impl bundle:
hello-paris-impl- Contains the private package,
org.fusesource.example.hello.paris.impl. Build this bundle to create the API/provider build-time combination bundle,hello-paris-impl. hello-paris- Contains the public API package,
org.fusesource.example.hello.paris. This project is a prerequisite forhello-paris-impland must be built (usingmvn install) before thehello-paris-implproject. You do not deploy thehello-parisbundle directly into the OSGi container, however.
Import and export rules Copy linkLink copied to clipboard!
hello-paris-impl bundle, when it is configured as an API/provider build-time combination:
- Exporting own packages—no own packages to export, because the
hello-paris-implMaven project contains only private implementation packages. - Exporting dependent packages—export the public API package,
org.fusesource.example.hello.paris, with theprovide:=trueclause, which causes the API code to be included in thehello-paris-implbundle at build time. - Importing own packages—import the public API package,
org.fusesource.example.hello.paris. - Importing dependent packages—any external package dependencies must be imported.
Maven bundle plug-in settings Copy linkLink copied to clipboard!
hello-paris bundle in the hello-paris-impl bundle, add the org.fusesource.example.hello.paris package to the Export-Package instruction with the provide:=true clause attached, as shown in Example 2.3, “Plug-In Settings for Build-Time Combination”. Compare this with the regular API/provider case, Example 2.2, “Plug-In Settings for API/Provider Combination”, which takes the same approach of exporting the API package with the provide:=true clause. The semantics, however, are a bit different, because the current example pulls in the API package from a separate Maven project.
Example 2.3. Plug-In Settings for Build-Time Combination
Assigning versions at package granularity Copy linkLink copied to clipboard!
org.fusesource.example.hello.paris API package is given the same version as the hello-paris-impl Maven project, by adding the version=${project.version} clause to the export instruction. In practice, however, you might not always want to assign versions in this way. You might prefer to assign distinct versions to the API package (from the hello-paris Maven project) and the implementation package (from the hello-paris-impl Maven project).
packageinfo file (see the section called “Export versions at package granularity” for details). The Maven bundle plug-in automatically scans your source code, looking for packageinfo files and extracting the version information from them. In this case, you must omit the version clause from the export instruction, as follows:
Generated MANIFEST.MF file Copy linkLink copied to clipboard!
MANIFEST.MF file:
Import-Package header imports both the public API package, org.fusesource.example.hello.paris, and the external package dependencies—for example, org.fusesource.example.time.
Export-Package header exports the public API package, org.fusesource.example.hello.paris, while the uses clause declares a transitive dependency on the org.fusesource.example.time package.
Export-Service header advertises the OSGi service as a HelloParis instance.
Bundle deployment Copy linkLink copied to clipboard!
hello-paris-impl bundle includes all of the code from the hello-paris Maven project, it is unnecessary to deploy the hello-paris bundle in this case.
2.8. Consumer Bundle Copy linkLink copied to clipboard!
Overview Copy linkLink copied to clipboard!
hello-consumer bundle exemplifies a consumer bundle, which imports OSGi services and accesses the services through the relevant API packages. This is the bundle that drives the sample application and, therefore, it relies on a blueprint lifecycle callback (through the blueprint bean element's init-method attribute) to initiate processing.
hello-consumer behaves as a pure consumer.
Directory structure Copy linkLink copied to clipboard!
hello-consumer bundle has the following directory structure:
org.fusesource.example.hello.consumer package is public and all of its classes and interfaces are exported from the bundle. It would not matter, however, if you made this package private instead, because it is not needed by any other bundles.
src/main/resources/OSGI-INF/blueprint directory contains a single blueprint file, client.xml. Any file matching the pattern, *.xml, in this directory is assumed to be a blueprint configuration file.
Sample consumer code Copy linkLink copied to clipboard!
hello-consumer bundle effectively drives the sample application, obtaining references to the HelloBoston and HelloParis OSGi services, and then invoking methods on these services to obtain localised greetings and times.
hello-consumer bundle contains the class, ConsumeHello, which is a client of the OSGi services, HelloBoston and HelloParis. To gain access to the OSGi services, ConsumeHello defines the setter methods, getHelloBoston() and getHelloParis(), and relies on the blueprint framework to inject the references. The entry point is the init() method, which gets invoked after the ConsumeHello bean is created and injected with the service references. The ConsumeHello class is defined as follows:
Access OSGi service Copy linkLink copied to clipboard!
ConsumeHello class needs to obtain a reference to the HelloBoston service and a reference to the HelloParis service. Use the reference element to create proxies for the HelloBoston service and for the HelloParis service. Use the bean element to create a ConsumeHello instance and inject the helloBoston and helloParis proxies.
ConsumeHello bean also requires an entry point to initiate processing. By setting the bean element's init-method attribute to init, you ensure that the blueprint framework calls the ConsumeHello.init() method after all of the bean's properties have been injected.
OSGI-INF/blueprint/client.xml, has the following contents:
Maven dependencies Copy linkLink copied to clipboard!
hello-consumer bundle defines dependencies on the following Maven artifacts:
time-utilhello-parishello-boston
Import and export rules Copy linkLink copied to clipboard!
hello-consumer bundle:
- Exporting own packages—a client typically does not need to export its own packages, because a client does not usually expose an API.
- Importing own packages—a client does not import its own packages.
- Importing dependent packages—any external package dependencies must be imported.
Maven bundle plug-in settings Copy linkLink copied to clipboard!
org.fusesource.example.hello.consumer, although the export is unnecessary in this particular example. The Export-Package instruction also contains entries to block the export of any packages containing .impl or .internal. In this case, the bundle plug-in instructions are as follows:
Generated MANIFEST.MF file Copy linkLink copied to clipboard!
MANIFEST.MF file:
Import-Package header imports the external package dependencies—for example, org.fusesource.example.hello.boston.
Export-Package header exports the package, org.fusesource.example.hello.consumer. In this case, however, the export is not really needed and the package could have been declared private instead (for example, using the Private-Package instruction).
Import-Service header declares the OSGi services accessed by this bundle. The services are accessed respectively through the HelloBoston interface and through the HelloParis interface.
Chapter 3. Versioning Copy linkLink copied to clipboard!
Abstract
3.1. Semantic Versioning Copy linkLink copied to clipboard!
Overview Copy linkLink copied to clipboard!
Version fidelity Copy linkLink copied to clipboard!
Backward compatibility in Java Copy linkLink copied to clipboard!
- Adding new fields, methods, or constructors to an existing class or interface.
- Deleting private fields, methods, or constructors of a class.
- Deleting package-only access fields, methods, or constructors of a class or interface.
- Re-ordering the fields, methods, or constructors in an existing type declaration.
- Moving a method upward in the class hierarchy.
- Reordering the list of direct super-interfaces of a class or interface.
- Inserting new class or interface types in the type hierarchy.
Consumers and providers Copy linkLink copied to clipboard!
- Consumer—a bundle that uses the functionality exposed by an API, invoking methods on the API's Java interfaces.
- Provider—a bundle that provides the functionality of an API, containing classes that implement the API's Java interfaces.
Figure 3.1. Consumers and Provider of API Interfaces
- Compatibility between consumer and API—this coupling obeys the rules from the Binary Compatibility chapter of the Java Language Specification. There is quite a lot of scope for altering the API without breaking backward compatibility; in particular, it is possible to add methods to Java interfaces without breaking compatibility.
- Compatibility between provider and API—this coupling is not explicitly considered in the Java Language Specification. It turns out that this coupling is much more restrictive than the consumer case. For example, adding methods to Java interfaces breaks backward compatibility with the provider.
Callback interfaces Copy linkLink copied to clipboard!
javax.jms.MessageListener interface from the JMS API, which is defined as follows:
onMessage() method in order to receive messages from the underlying JMS service. So, in this case, the more restrictive binary compatibility rules (the same ones that apply to a provider) must be applied to the MessageListener interface.
OSGi version syntax Copy linkLink copied to clipboard!
<major> [ '.' <minor> [ '.' <micro> [ '.' <qualifier> ]]]
<major> [ '.' <minor> [ '.' <micro> [ '.' <qualifier> ]]]
<major>, <minor>, and <micro> are positive integers and <qualifier> is an arbitrary string. For example, the following are valid OSGi versions:
4 2.1 3.99.12.0 2.0.0.07-Feb-2011
4
2.1
3.99.12.0
2.0.0.07-Feb-2011
OSGi version ranges Copy linkLink copied to clipboard!
[ and ]— denote an inclusive range; and parentheses—that is, ( and )—denote an exclusive range. You can also mix a parenthesis with a bracket to define a half-inclusive range. Here are some examples:
| OSGi Version Range | Restriction on Version, v |
|---|---|
[1.0, 2.0) | 1.0 <= v < 2.0 |
[1.0, 2.0] | 1.0 <= v <= 2.0 |
(1.4.1, 1.5.5) | 1.4.1 < v < 1.5.5 |
(1.5, 1.9] | 1.5 < v <= 1.9 |
1.0 | 1.0 <= v < ∞ |
[1.0, 2.0). A simple version number on its own, 1.0, is interpreted as an inclusive range up to positive infinity.
Semantic versioning rules Copy linkLink copied to clipboard!
<major>- When a change breaks binary compabitility with both consumers and providers, increment the bundle's major version number. For example, a version change from
1.3to2.0signals that the new bundle version is incompatible with older consumers and providers. <minor>- When a change breaks binary compatibility with providers, but not consumers, increment the bundle's minor version number. For example, a version change from
1.3to1.4signals that the new bundle version is incompatible with older providers, but compatible with older consumers. <micro>- A change in micro version does not signal any backward compatibility issues. The micro version can be incremented for bug fixes that affect neither consumers nor providers of the API.
<qualifier>- The qualifier is typically used as a build identifier—for example, a time stamp.
Consumer import range Copy linkLink copied to clipboard!
1.3, the next version that would break binary compatibility with the consumer is 2.0. It follows that all exporter versions up to, but excluding, 2.0 ought to be compatible with the consumer.
<major>.<minor>.<micro>.<qual>, define the corresponding consumer import range to be [<major>.<minor>, <major>+1).
| Build-Time Version of Exporter | Consumer Import Range |
|---|---|
3.0 | [3.0, 4) |
2.0.1 | [2.0, 3) |
2.1.4 | [2.1, 3) |
2.1.5.2011-02-07-LATEST | [2.1, 3) |
Provider import range Copy linkLink copied to clipboard!
1.3, the next version that would break binary compatibility with the provider is 1.4. It follows that all exporter versions up to, but excluding, 1.4 ought to be compatible with the provider.
<major>.<minor>.<micro>.<qual>, define the corresponding provider import range to be [<major>.<minor>, <major>.<minor>+1).
| Build-Time Version of Exporter | Provider Import Range |
|---|---|
3.0 | [3.0, 3.1) |
2.0.1 | [2.0, 2.1) |
2.1.4 | [2.1, 2.2) |
2.1.5.2011-02-07-LATEST | [2.1, 2.2) |
3.2. Export Versioning Copy linkLink copied to clipboard!
Overview Copy linkLink copied to clipboard!
Export version at bundle granularity Copy linkLink copied to clipboard!
${project.version} macro returns the the contents of the project/version element in the POM file (the version of the current Maven artifact).
Export versions at package granularity Copy linkLink copied to clipboard!
packageinfo file in the corresponding package directory. For example, if you want to assign version 1.2.1 to the org.fusesource.example.time package, create a file called packageinfo (no suffix) in the src/main/java/org/fusesource/example/time directory and add the following line:
version 1.2.1
version 1.2.1
package-info.java file, for example:
@Version("1.2.1")
package org.fusesource.example.time;
@Version("1.2.1")
package org.fusesource.example.time;
3.3. Automatic Import Versioning Copy linkLink copied to clipboard!
Overview Copy linkLink copied to clipboard!
Automatic consumer import range Copy linkLink copied to clipboard!
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)"
Import-Package: org.fusesource.example.time;version="[1.0,2)"
Automatic provider import range Copy linkLink copied to clipboard!
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 Copy linkLink copied to clipboard!
provide:=true clause to the relevant API packages listed in the Import-Package instruction.
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:
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)"
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 Copy linkLink copied to clipboard!
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).
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:
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"
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"
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 Copy linkLink copied to clipboard!
<Import-Package> com.package.with.wrong.semantics*;version="[1.3,1.3.4]", * </Import-Package>
<Import-Package>
com.package.with.wrong.semantics*;version="[1.3,1.3.4]",
*
</Import-Package>
Custom import rule Copy linkLink copied to clipboard!
range macro to codify this rule on consumer imports, as follows:
<Import-Package><![CDATA[ com.package.with.wrong.semantics*;version="$<range;[==,=+)>", * ]]> </Import-Package>
<Import-Package><![CDATA[
com.package.with.wrong.semantics*;version="$<range;[==,=+)>",
*
]]>
</Import-Package>
$<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).
== 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.
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>
<Import-Package><![CDATA[
com.package.with.wrong.semantics*;version="[$<version;==>,$<version;=+>)",
*
]]>
</Import-Package>
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 Copy linkLink copied to clipboard!
-consumer-policy- Specifies a macro to calculate the consumer import range.
-provider-policy- Specifies a macro to calculate the provider import range.
_consumer-policy and _provider-policy—for example:
$<Macro>, is escaped as, $<Macro>, in XML (or you could use a CDATA section).
-, 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, _.
Legal Notice Copy linkLink copied to clipboard!
Trademark Disclaimer