Guidance for using OSGi

A number of considerations for developing OSGi applications.

Defining dependencies

When an OSGi bundle uses Java™ packages from another OSGi bundle, the interface between the two bundles must be explicitly expressed. The bundle that uses the package must add the package to the Import-Package statement in its manifest.mf. The bundle that provides the package must add the package to the Export-Package statement in its manifest.mf. When both OSGi bundles are deployed into the environment, the dependency can be resolved.

All packages that are used by an OSGi bundle, including JRE extensions such as javax.* must be explicitly imported. This is the case even if the run time would otherwise find these packages through other means such as bootdelegation. Assume that only the core java.* packages are available by default.

Versioning of JCICS dependencies: The com.ibm.cics.server package (JCICS) will increment in version number when there are API additions, API removals, or bug-fixes during service or development work. However, version increments are not guaranteed on a release boundary. Versions, and which CICS® release they apply to, are described in Package com.ibm.cics.server.

It is prudent to declare Imports as a compatible range, beginning at your applications minimum supported level, up to (but not including), the next breaking API change. For example: Import-Package: com.ibm.cics.server;version="[2.0.0,3.0.0)".

There are alternative ways of expressing dependencies - in particular the bundle header Require-Bundle. However, Require-Bundle is more coarse-grained and ties the consumer to a specific bundle. Using Require-Bundle also prevents architectural flexibility and restricts the ability to version packages independently.

JCICS restrictions in OSGi bundles

The JCICS API classes have these restrictions when used in OSGi bundles:

  • JCICS API calls cannot be used in the activator classes of OSGi bundles.
    Note: The Java thread that runs the OSGi bundle activator will not be JCICS-enabled. A developer can start a new JCICS-enabled thread from an activator, by using the CICSExecutorService.runAsCICS() method. Any JCICS commands will run under the authority of the user ID that issued the install command. Therefore, it is prudent for an administrator to understand the resources used in OSGi bundle activators before they install them. For more information on how to use the runAsCICS() method, see Threads and tasks example.
  • Start and stop methods used in OSGi bundle activators must return in a reasonable amount of time.

JRE class visibility, bootdelegation, and system.packages.extra

In OSGi, loading of core JRE packages/classes (java.*) is always delegated to the bootstrap classloader. It is assumed that there is only one JRE in the system, and so explicit dependency statements are not required. For that reason, it is never necessary to add a java.* dependency to a bundle manifest. However, for other parts of the JRE, application bundles that require these packages must code an Import-Package statement; for example vendor-specific extensions javax.* com.sun.* and com.ibm.* require an import. This is because they are not delegated to the bootstrap classloader and instead treated as part of the OSGi system.

The OSGi framework provides a system bundle that exposes known extension packages to the system automatically. The application bundle registers its dependency by including an Import statement, just as for all other packages provided by OSGi bundles. The advantage of this approach is that extensions can be replaced with newer implementations by installing an OSGi bundle that contains the new code.

An exception to this process is where a particular package is added to the bootdelegation list by using a special OSGi property. Although convenient (as no Import statement is required to access these packages), it restricts the flexibility of OSGi and is not considered best practice. Occasionally there are vendor-specific extensions that aren't automatically added to the system bundle by the OSGi implementation. For these cases, and assuming the package is genuinely available from the JRE, the property -Dorg.osgi.framework.system.packages.extra can be used to add the packages to the system bundle and allow application Imports to resolve.

Bundle activators

Bundle activators are classes within an OSGi bundle that implement the BundleActivator interface. To use an activator, an OSGi bundle must declare it using the Bundle-Activator header in the bundle manifest. The BundleActivator interface has start and stop methods that can be used to perform initialization or termination work. A common pattern is to look up service dependencies for use within the application. However, it is better to employ a component model, such as Declarative Services to activate components and their service dependencies.

Singleton bundles

A singleton bundle is used to prevent any other version of a bundle being loaded in memory, there can be only one resolved version in the run time at any point. The use of a singleton bundle can be desirable where access to a single system resource is required from a set of applications.

OSGi bundle fragments

Fragments are OSGi bundles that are dynamically attached to host bundles by the OSGi framework. They share the class loader for their host bundle, and do not participate in the lifecycle of the bundle - for that reason they do not support bundle activators. Common use-cases for fragments are as bundle patches. A fragment provided ahead of . on the Bundle-ClassPath allows classes to be preferentially loaded from the fragment instead of the host.

OSGi service registry

The OSGi service registry enables a bundle to publish objects to a shared registry. A service is advertised under a Java interface and made available to other bundles installed in the OSGi environment.

Microservices (μServices)

Microservices are a software architecture style in which complex applications are composed of small, independent components which communicate with each other using language-agnostic APIs. These services are small, highly decoupled, and focus on doing a small task, facilitating a modular approach to system building. The use of μServices between OSGi components provides flexibility and dynamic update capabilities that cannot be achieved by using bundle wiring alone. For this reason, the use of μServices is encouraged over bundle-wiring.

Bundle and package versioning

A favored approach to package versioning in OSGi is the semantic versioning model. Given a version number MAJOR.MINOR.PATCH, increment the:
  1. MAJOR version when you make incompatible API changes
  2. MINOR version when you add functionality that is compatible with an earlier version
  3. PATCH version when you make bug fixes that are compatible with an earlier version

Execution environment

Execution environments (EEs) are symbolic representations of JREs, for example:
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
You need to use the lowest version of EE that gives you all the features you require. When creating a new OSGi bundle, the most recent actively maintained Java execution environment is usually adequate - only if a specialized application requires a lower version would you set it at a lower level. When a particular EE is chosen, it must be left alone unless there is a clear advantage to moving up. Increasing the version of your EE can create more work with no real value, such as exposing your code to new warnings, and deprecations.