Oleg’s Weblog

My Tech Rant

Understanding OSGi uses directive

Posted by Oleg Zhurakousky on December 9, 2008

Introduction
In current Java model, uniqueness of the class is defined by fully qualified name of the class plus the defining class loader. This mechanism could potentially create a problem, where it is quite easy to have multiple versions of the same class loaded in a single JVM space. This happens quite a lot, giving Java’s ability to load classes dynamically. Modern framework such as Spring, Hibernate and others utilize this feature quite a bit. However if not used properly, having multiple instances of class loaded often results in memory leaks and various class type exceptions such as CCE, NCDF, Linkage etc. . .

OSGi approaches this problem rather differently and it does so by bridging the concept of class/packages version with the runtime environment, thus still allowing you to have multiple versions of the same class loaded without creating conflicts.  But it comes with the price. OSGi defines a very strict dependency resolution and class sharing rules that must be explicitly declared, while at the same time moving us a way from the legacy mentality of the linear class path we are all so used to.

This article concentrates on one specific area of OSGi dependency resolution mechanism and that is “uses” directive.

Prerequisite
It is assumed that user is familiar with basics of OSGi and how to develop/deploy OSGi bundles using Eclipse PDE. The source code examples (provided below) are packaged as Eclipse PDE projects. Simply import them into the workspace.

The sample sources are distributed in two versions: Initial state and Solution state which represents all completed steps of this article.

NOTE: To avoid issues with Eclipse caching when setting up your OSGi Target Platform add this to the VM arguments -Dosgi.clean=true

There are 6 bundle project distributed with this article

  • Bundle org.packageadmin.util – contains simple utility class which allows bundle programmers to inspect the package wiring state of bundles and is based on PackageAdmin Service of OSGi.
  • Bundle org.transportation-v1.0/v2.0 – this bundle represents an imaginary business components which must figure out Transportation allowance based on the amount of travelers provided. NOTE: there is no business logic; you can implement it as you wish. These bundles exist in two versions and although functionality is the same, the versioning of the bundles and exported/imported packages is enough to achieve the goal of this article.
  • Bundle org.tripplanner-v1.0 /v2.0 – this bundle represents an imaginary business component which must figure out Trip based on Transportation and distance provided. NOTE: there is no business logic either; you can implement it as you wish. These bundles exist in two versions and although functionality is the same, the versioning of the bundles and exported/imported packages is enough to achieve the goal of this article.
  • Bundle org.travel.agent – represents an imaginary business component which will work with org.transportation and org.tripplanner and bundles.

Scenario-1

♦ Deploy the following bundles:

  • org.packageadmin.util
  • org.transportation-v1.0/v2.0
  • org.tripplanner-v1.0 /v2.0

As we see in this example in the OSGi Target Platform which runs in the single instance of JVM, two instances of org.transportation and org.tripplanner bundle exist happily without creating any conflicts for one another. Something that would require a lot of plumbing work (e.g., writing custom class loaders), comes free with OSGi.  It is possible based on the fact that org.transportation-v1.0 bundle forms a single Class space with org.tripplanner- v1.0 bundle based on Import/Export-Package declarations in MANIFESTs of these bundles. The same happens in versions 2.0 of the corresponding bundles (see Figure 1)

uses12

Figure 1

Scenario-2

However what would happen if we were to add a third bundle into the mix? We’ll call this bundle org.travel.agency. This bundle will represent a service that you might want to distribute. This service will be able to plan the entire trip, whatever that might be. The point here is that this service will be using API exported by both org.transportation bundle and org.tripplanner bundle. Bundle org.tripplanner has a class TripPlanner which exposes the method planTrip(Transportation), which will calculate the trip based on the existing Transportation object created by TransportationCalculator and exposed by org.transportation bundle via Export-Package declaration. The Transportation object represents transportation allowance based on the amount of travelers (see code for more details).  The basic structure of these 3 bundles is shown in Figure 2

uses2

Figure 2

♦ Deploy org.travel.agency bundle along with the rest of the bundles.

You should not see any issues. Everything should work as expected. By deploying all these bundles, we can see that org.travel.agency bundle is resolved to the latest available packages  exported by org.transportation and org.tripplanner which is version 2.0 (use bundle command in OSGi console for more info). Remember that although org.travel.agency explicitly depends on version=”1.0” via Import-Package directive, single version specification in OSGi means resolve to the latest available package, where “at least” version is specified.

Scenario-3

However, let’s assume that the developers of org.tripplanner-v1.0 bundle weren’t taking any chances with possible future releases of org.transportation bundle and specified strict dependency on org.transportation package version 1.0.

♦ Change the Import-Package statements of org.tripplanners bundles to reflect the strict versioning specification (e.g., org.transportation;version=”[1.0, 1.0]” for Bundle Version 1.0 and org.transportation;version=”[2.0, 2.0]” for Bundle Version 2.0 )

Meanwhile our org.travel.agency bundle is less restrictive and can easily use either version of org.transportation package.
To replicate this:

♦ Deploy two versions of org.transportation bundle.

♦ Deploy ONLY org.tripplanner-v1.0.

Deploy org.travel.agency bundle.

When deployed, use bundle command in OSGi console to inspect bundle’s resolution state. You will clearly see that org.tripplanner bundle is resolved to org.transportation-v1.0 bundle, while org.travel.agency bundle is resolved to org.transportation-v2.0 bundle. Figure 3 depicts the resolution state of these bundles in this scenario.

uses3

Figure 3

Using “ss” command in OSGi console you can also see that org.travel.agency bundle is in the RESOLVED state.  Attempt to start this bundle manually will result in LinkageError.
The problem is quite simple. There is a conflict in the Class space. The conflict is triggered by the fact that in the current state of the class space there are two bundles that export org.transportation package. Due to the local constraints, org.tripplanner bundle is using org.transportation package v1.0, while org.travel.agency bundle is using org.transportation package v2.0.  This creates multiple exporters for package org.transportation, thus violating class space consistency which can only be ensured if there is only one exporter for each package.

Scenario-4

You could easily fix it by specifying a strict dependency on org.transportation;version=”[1.0, 1.0]” inside of org.travel.agency bundle. In our example it would be quite simple however, if you have a hierarchy of dependent bundles, you would have to manually interrogate each and every one of them to determine all strict dependencies before you can ensure class space consistency. Thankfully there is a better approach, where bundle developers can now identify dependency on specific package constraint ahead of time.
To accomplish this issue OSGi defines “uses” directive
“If a bundle imports a package from an exporter then the export definition of that package can imply constraints on a number of other packages through the “uses” directive”OSGi Specification R4.
So, in our situation all we need to do is make sure that org.tripplanner bundle declares “uses” directive as part of its Export-Package declaration, which will help to ensure that all other bundles which might depend on org.tripplanner and org.transportation will all resolve to the same version of org.transportation. In other words in the current scenario org.tripplanner bundle will ensure propagation of its strict dependency on org.transportation-v1.0. Figure 4 documents this:
uses4

Figure 4

Bundle org.tripplanner depends on version 1.0 of org.transportation package via Import-Package directive. While exporting org.tripplaner package, “uses” directive allows bundle developer to explicitly specify that the exported package must use only the version of the package imported by the bundle and not any other. While constructing class space OSGi will make sure that, if another bundle (e.g., org.travel.agency) which depends on the packages of the dependent bundle (e.g., org.tripplanner) as well as packages that dependent bundle is using (e.g., org.transportation), there is only one exporter for the shared package (e.g., org.transportation).

♦ Provide “uses” directive and restart your OSGi Target Platform

Example:   Export-Package: org.tripplanner;version=”1.0”;uses:org.transportation

All deployed bundles should be in ACTIVE state. While inspecting the state of the org.travel.agency bundle you should see that it is now resolved to the version 1.0 of org.transportation package.

Scenario-5

But what if org.travel.agency specified strict dependency on version 2.0 of org.transportation package. This would re-create the conflict similar to the one we’ve seen in Figure 3. However this conflict would be unresolvable, since each bundle would specify dependency on the specific version of a package, but specified version of such package is different for both bundles. The fact that org.tripplanner bundle “uses” org.transportation-v1.0 makes resolution of org.travel.agency bundle impossible, thus keeping this bundle in the INSTALLED state.

♦ Modify Import-Package statement of org.travel.agent budle to specify strict dependnecy on version 2.0 of org.transporation package

Example:   org.transportation;version=”[2.0, 2.0]”

Any attempt to start this bundle manually will result in the exception:


org.osgi.framework.BundleException: The bundle could not be resolved. Reason: Package uses conflict: Import-Package: org.tripplanner; version="1.0.0"
at org.eclipse.osgi.framework.internal.core.BundleHost.startWorker(BundleHost.java:305)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:265)
. . . . . . . . . . . . . . . . .

🙂

Advertisements

3 Responses to “Understanding OSGi uses directive”

  1. […] following article article concentrates on one area of OSGi dependency resolution mechanism and that is “uses” […]

  2. Joel Costigliola said

    Hi Oleg,

    Nice article !
    I have wanted to signal you few typo/mistakes (nothing serious anyway).
    In the text under figure 4 : by the bundle and not any oter -> other
    You also use ‘org.travel.agent’ instead of ‘org.travel.agency few times’.

    Best regards,

    Joel

  3. Oleg Zhurakousky said

    Thanks Joel
    I’ve corrected them.
    Oleg

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: