2.4 Modularity
An OSGi bundle provides a clear definition of its modularity—this includes its identity, its requirements, and its capabilities. The Bundle-SymbolicName and Bundle-Version manifest headers take care of defining identity. A bundle can have a number of different capabilities and requirements. The most common pattern is to express these dependencies in terms of Java packages. Bundle developers can also specify dependencies on whole bundles.
2.4.1 Exporting a Package
To give access to Java types in a bundle, the bundle must export the package containing the types; that is, OSGi's unit of Java dependency is the Java package. Bundles can export any number of packages. By exporting a package, the bundle is saying that it is able and willing to supply that package to other bundles. Exported packages form the public API of the bundle. Packages that are not exported are considered to be private implementation details of the bundle and are not accessible to others. This is a powerful concept and one of the reasons that OSGi's component model is so appealing.
A bundle that uses the Export-Package header to export several packages is shown in the following manifest snippet. Notice that the packages are specified in a comma-separated list and that a version number can be specified for each package. Each package is versioned independently.
org.equinoxosgi.toast.core/MANIFEST.MF Bundle-SymbolicName: org.equinoxosgi.toast.core Bundle-Version: 1.0.0 Export-Package: org.equinoxosgi.toast.core;version=1.2.3, org.equinoxosgi.toast.core.services;version=8.4.2
2.4.2 Importing a Package
Exporting a package makes it visible to other bundles, but these other bundles must declare their dependency on the package. This is done using the Import-Package header.
The following manifest snippet shows a bundle that imports several packages. As with exports, the set of imported packages is given as a comma-separated list. Notice here that the import for each package can be qualified with a version range. The range specifies an upper and lower bound on exported versions that will satisfy the requirements of this bundle. Versions, version ranges, and dependency management are discussed throughout the book as they form a key part of developing, maintaining, and deploying modular systems.
org.equinoxosgi.toast.core/MANIFEST.MF Bundle-SymbolicName: org.equinoxosgi.toast.core Bundle-Version: 1.0.0 Import-Package: org.osgi.framework;version="[1.3,2.0.0)" org.osgi.service.cm;version="[1.2.0,2.0.0)"
2.4.3 Requiring a Bundle
It is also possible to specify a dependency on an entire bundle using a Require-Bundle header, as shown in the following manifest fragment:
org.equinoxosgi.toast.dev.airbag.fake/MANIFEST.MF Bundle-Name: Toast Fake Airbag Bundle-SymbolicName: org.equinoxosgi.toast.dev.airbag.fake Bundle-Version: 1.0.0 Import-Package: org.eclipse.core.runtime.jobs, org.equinoxosgi.toast.core;version="[1.0.0,2.0.0)", org.equinoxosgi.toast.dev.airbag;version="[1.0.0,2.0.0)" Require-Bundle: org.eclipse.equinox.common; bundle-version="3.5.0"
With this approach, a bundle is wired directly to the prerequisite bundle and all packages it exports. This is convenient but reduces the ability to deploy bundles in different scenarios. For example, if the required bundle is not, or cannot be, deployed, the bundle will not resolve, whereas the actual package needed may be available in a different bundle that can be deployed.
Requiring bundles can be useful when refactoring existing systems or where one bundle acts as a façade for a set of other bundles. Requiring a bundle also allows for the specification of dependencies between modules that do not deliver Java code and so do not export or import packages.
2.4.4 Enforcing Modularity
Given these capability and requirements statements, the OSGi framework resolves the dependencies and wires bundles together at runtime. Modularity in an OSGi system is enforced through a combination of wires and standard Java language visibility rules. To manage this, the framework gives each bundle its own class loader. This keeps separate the classes from the different bundles. When a bundle is uninstalled or updated, its class loader, and all classes loaded by it, are discarded. Having separate class loaders allows the system to have multiple versions of the same class loaded simultaneously. It also enforces the standard Java type visibility rules, such as package visible and public, protected and private, in a bundle world.