Guidance for using OSGi
A number of considerations for developing OSGi applications.
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.
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="[1.800.0,2.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
activatorclasses 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
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
dependency to a bundle manifest. However, for other parts of the
application bundles that require these packages must code an Import-Package
statement; for example
require an import. This is because they are not delegated to the
bootstrap classloader and instead treated as part of the OSGi
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
list by using a special OSGi property. Although convenient (as no
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
these cases, and assuming the package is genuinely available from the
JRE, the property
can be used to add the packages to the
system bundle and allow application
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.
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
A fragment provided ahead of
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 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
- MAJOR version when you make incompatible API changes
- MINOR version when you add functionality that is compatible with an earlier version
- PATCH version when you make bug fixes that are compatible with an earlier version
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.