OSGi upgrade strategies

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.

Embedding the library directly

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.

Note

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.

Wrapping the library with bnd

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.

Note

Note that commons-logging Version 1.1 and above already have support for OSGi, and they should be used instead for Java applications. The older version is being used to demonstrate how to add OSGi metadata to a publicly available library that doesn't already have it.

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               

Note

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).

Upgrading the library to use services

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.

Dealing with class resolution issues

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:

  • Pass in 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.
  • Pass in a 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.
  • Wrap calls to the library with an appropriate 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.
  • Add 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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset