While many open source libraries already natively support OSGi, there are still some that do not have the required metadata added by default. Fortunately, there are a number of strategies that can be used to enable the libraries to be used in OSGi runtimes such as Eclipse, or repositories such as Eclipse Orbit (http://eclipse.org/orbit/) that contain corrected bundles.
If the library is only needed in one bundle, then the most expedient mechanism is to embed the JAR(s) into that bundle. Since all libraries in a bundle share the same classpath, this allows the library to run without being aware of the fact that it is running in an OSGi environment. This can be done by embedding the JAR into the bundle and using a Bundle-ClassPath
header that refers to the library:
Bundle-ClassPath: .,lib/example.jar
Note that the dot (.
) must be present if the bundle itself contains classes, or to enable other resources to be loaded. This approach works well for Hibernate (before version 4.2) or other ORM tools that do not work well with multiple ClassLoaders.
With an embedded library, it is also possible to export a subset of packages made visible by the library itself. This allows only the public API to be exposed while hiding the internal API from users.
Embedding a library is an expedient way of testing the bundle without having to rebuild the JAR. Sometimes, it is useful if the library is signed or cannot be changed, as mutating a signed JAR leads to problems in the Eclipse runtime.
Note that embedded JARs are slightly less performant, as the JAR needs to be extracted from the library at runtime and made available on the filesystem in order to load resources from it. As a result, this is not the preferred way to solve the problem.
An extension to merely embedding, wrapping the library also adds additional Export-Package
and Import-Package
headers to allow the library to resolve. This permits other bundles to import packages exported by the library bundle and use it as a standard library.
If the library is minimal, the bundle's manifest can be calculated manually. However, it is more efficient to use an automated tool such as bnd, which can be downloaded from Maven Central at https://repo1.maven.org /maven2/biz/aQute/bnd/bnd/2.2.0/bnd-2.2.0.jar.
The bnd tool allows a JAR to be processed and entries for the used Import-Package
and Export-Package
calculated based on the contents of the classes. It is used indirectly by most of the build tools to generate valid OSGi metadata.
As an example, consider upgrading commons-logging-1.0.4
, available from https://repo1.maven.org
/maven2/commons-logging/commons-logging/1.0.4/commons-logging-1.0.4.jar.
The bnd tool provides a means to print an existing JAR's manifest:
$ java -jar bnd-2.2.0.jar commons-logging-1.0.4.jar [MANIFEST commons-logging-1.0.4] Ant-Version Apache Ant 1.5.3 Created-By Blackdown-1.3.1_02b-FCS Extension-Name org.apache.commons.logging Implementation-Vendor Apache Software Foundation Implementation-Version 1.0.4 Manifest-Version 1.0 Specification-Vendor Apache Software Foundation Specification-Version 1.0
In this case, it can be seen that it does not have any OSGi data associated with it. There is no Bundle-SymbolicName
or Bundle-ManifestVersion
.
The bnd tool can calculate the set of required dependencies with the wrap
command:
$ java -jar bnd-2.2.0.jar wrap --output commons-logging-1.0.4.osgi.jar --bsn commons-logging commons-logging-1.0.4.jar ----------------- Warnings 000: Using defaults for wrap, which means no export versions $ java -jar bnd-2.2.0.jar print commons-logging-1.0.4.osgi.jar [MANIFEST commons-logging-1.0.4.osgi] Ant-Version Apache Ant 1.5.3 Bnd-LastModified 1390176259029 Bundle-ManifestVersion 2 Bundle-Name commons-logging Bundle-SymbolicName commons-logging Bundle-Version 0 Created-By 1.7.0_45 Export-Package org.apache.commons.logging, org.apache.commons.logging.impl; uses:="org.apache.avalon.framework.logger, org.apache.commons.logging,org.apache.log,org.apache.log4j" Extension-Name org.apache.commons.logging Implementation-Vendor Apache Software Foundation Implementation-Version 1.0.4 Import-Package org.apache.avalon.framework.logger;resolution:=optional, org.apache.log;resolution:=optional, org.apache.log4j;resolution:=optional Manifest-Version 1.0 Originally-Created-By Blackdown-1.3.1_02b-FCS Specification-Vendor Apache Software Foundation Specification-Version 1.0 Tool Bnd-2.2.0.20130927-173453
The generated bundle can now be installed in an OSGi environment. Although this provides a starting point for wrapping a non-OSGi bundle, typically the manifest will require additional adjustments. In this case, the org.apache.log4j
package may be a non-optional dependency of the commons-logging
interface and so should be updated; in addition, the Bundle-Version
should really be 1.0.4
to correspond to the original version of the upstream JAR. Additional operations can be supplied on the bnd
command line or by providing an additional .bnd
configuration file. Documentation is available at the Bnd homepage at http://www.aqute.biz/Bnd/Bnd.
Bnd can also be used to add additional metadata to the bundle, such as headers to enable Declarative Services (covered in Chapter 3, Using OSGi Services to Dynamically Wire Applications) or service mediator requirements (see the Java ServiceLoader section earlier in this chapter).
Many libraries are pure library code; they provide no implementation or services for consumers. These can often be wrapped/exported to provide packages for other bundles to depend upon.
However, if the library is expected to provide a service, there may be an advantage in exposing this as an OSGi service. That way, other OSGi bundles can depend on the service itself rather than a particular provider of that service.
The easiest way to publish services is to use either Declarative Services or Blueprint (see Chapter 3, Using OSGi Services to Dynamically Wire Applications, for more details). By adding the appropriate service XML files and adding the right manifest headers, no code needs to be written and the library can publish services automatically. When used in combination with Config Admin, the library can have new services published or updated as configuration data changes without needing to write any code.
Most libraries do not need to refer to class names directly. However, a subset of them will need to look up classes, typically if they are doing deserialization or parsing from a database or stream. There are four ways to handle this:
Class
instances instead of class names into the library. This is the most portable way of resolving the problem, and this works for many APIs, especially those using annotations to expose information.ClassLoader
along with the class names, which can be acquired from the calling bundle's context. This will allow the library to acquire classes from the correct ClassLoader
.threadContextClassLoader
set. This is likely to be the least efficient way of solving the problem but may at least allow a library to work in some situations.DynamicImport-Package:*
to the library. This will allow the library to wire up dependencies as it attempts to look them up. This will also prevent bundle re-loading in the same VM as it will pin the version of the client to the library. This is not recommended as a general practice; use this only as a last resort.If none of the mentioned ways are appropriate, consider using a different library. Some libraries are sufficiently broken that they cannot be used in a dynamic environment such as Eclipse or OSGi. It is quite likely that the same problems will exist for these kinds of libraries in other multiple ClassLoader
environments such as web application servers.