The road to hell is paved with unsatisfied dependencies

OSGi bundles don’t share code arbitrarily like ordinary jars. Instead they explicitly export the packages they want to share and import the packages they need. Before you can use code from a bundle, it has to be resolved by the OSGi framework. Resolution involves matching each package a bundle imports with a package exported by some provider bundle. The provider bundle also has to be resolved, so the whole process is recursive.

Manually working out which bundles you need to install before the bundle you are actually interested in will resolve can be a long winded process in which you install the bundle you want, try to resolve it, fail, add more bundles in response to the error messages, fail, tear out some hair, add more bundles, and repeat, until finally you get something that works. At this point you can look at the resolved set of bundles and make a note of them and the order you installed them in to make it work. Even then the result is quite brittle because each resolution is sensitive to the state of the framework and to bundle versions. To cut a long story short, doing things this way leads straight to the OSGi dungeon in dependency hell.

Of course, it doesn’t have to be this way. We can automate the resolution process just like Apt and Yum automate Linux installs. This is where standalone resolvers like Nimble come in. They use repository metadata to figure out and install sets of bundles that will resolve successfully, relieving developers of this burden.

Writing a good resolver turns out to be a real challenge. In fact the problem is NP Complete, and naive, brute force implementations can easily get lost in resolution searches that take weeks or more (I’m not joking). More sophisticated solutions, using powerful heuristics, can do the same job in an instant.

Fortunately, the time spent developing a powerful resolver is well worth it because, as well as solving the original problem, it can be re-targeted to address other dependency concerns.

Active bundle dependencies

Nimble’s resolver is the third generation of Paremus’ resolver technology. One of its design goals is to move beyond resolution of bundle imports and exports, and enable automatic resolution for a variety of different dependency types.

To see how useful this can be, let’s take a look at ACTIVE bundle dependencies. These are bundle dependencies that only apply to bundles in their ACTIVE state.

For example, suppose you have a bundle, HelloDS, that makes use of Declarative Services (DS), and a DS runtime bundle. In its RESOLVED state HelloDS may well have no dependency on the DS runtime bundle whatsoever. However, once you activate HelloDS, it wont work properly unless the DS runtime bundle has also been installed and activated. So, in its ACTIVE state, the HelloDS bundle depends on the DS runtime provided by ACTIVE state of the DS runtime bundle. Note also that the ACTIVE state of any bundle depends on the RESOLVED state of the same bundle. This is all illustrated below.

When you ask Nimble to index a bundle repository it analyses the bundles, detects a wide variety of dependency types, and associates these with the appropriate bundle states. This gives Nimble its unique ability to resolve ACTIVE bundle dependencies, such as the need for the Spring DM, Declarative Services or iPOJO runtimes. It is also the basis of Nimble’s ability to install console commands on demand the first time you use them.

To make this concrete I’m going to work through a simple example using a HelloDS bundle, just like the one I mentioned above. For something more sophisticated, take a look a David Savage’s recent post on Zen and the Art of OSGi Dependency Resolution.

First of all let’s launch Nimble.

$ posh
Internal Paremus Nimble Developer License,
expires Fri Dec 31 23:59:59 GMT 2010.
________________________________________
Welcome to Paremus Nimble!
Type 'help' for help.
[Pequod.local.0]%

Throughout this demo you may see some bundles and other repository files downloading – I’ve omitted these lines for clarity.

Before going any further you should check that you have the latest version of Nimble, and the latest Paremus-hosted repository indexes by typing

% version

If you need an update this will tell what to type to get it.

Next we’ll load the examples repository that contains the HelloDS bundle, and look at the state of the base container.

% repos -l examples
% lsb
0  ACTIVE    org.eclipse.osgi:3.5.1.R35x_v20090827
1  ACTIVE    com.paremus.posh.runtime:1.0.11
2  ACTIVE    com.paremus.posh.shell:1.0.11
3  ACTIVE    com.paremus.posh.readline:1.0.11
4  RESOLVED  com.paremus.util.types:1.0.11
5  ACTIVE    com.paremus.nimble.core:1.0.11
6  ACTIVE    com.paremus.nimble.repos:1.0.11
7  ACTIVE    com.paremus.nimble.cli:1.0.11
8  ACTIVE    com.paremus.util.cmds:1.0.11

Now let’s ask Nimble to RESOLVE the HelloDS bundle, then list the installed bundles:

% add HelloDS@resolved
[Pequod.local.0]% lsb
0  ACTIVE    org.eclipse.osgi:3.5.1.R35x_v20090827
1  ACTIVE    com.paremus.posh.runtime:1.0.11
2  ACTIVE    com.paremus.posh.shell:1.0.11
3  ACTIVE    com.paremus.posh.readline:1.0.11
4  RESOLVED  com.paremus.util.types:1.0.11
5  ACTIVE    com.paremus.nimble.core:1.0.11
6  ACTIVE    com.paremus.nimble.repos:1.0.11
7  ACTIVE    com.paremus.nimble.cli:1.0.11
8  ACTIVE    com.paremus.util.cmds:1.0.11
9  RESOLVED  javax.servlet:2.5.0.v200806031605
10  RESOLVED  osgi.cmpn:4.2.0.200908310645
11  RESOLVED  HelloDS:1.0.0

Here bundle 11 is the one we asked to resolve, and bundles 9 and 10 were brought in to support its resolution. Note that there’s no sign of the DS runtime bundle, because it’s not needed to satisfy any package level dependencies.

Nimble is a full lifecycle resolver, i.e. it doesn’t just install bundles, it can also clean up reliably, so now that we’re done, we can get back to where we started by simply removing the resolved state HelloDS bundle.

% remove HelloDS@resolved
[Pequod.local.0]% lsb
0  ACTIVE    org.eclipse.osgi:3.5.1.R35x_v20090827
1  ACTIVE    com.paremus.posh.runtime:1.0.11
2  ACTIVE    com.paremus.posh.shell:1.0.11
3  ACTIVE    com.paremus.posh.readline:1.0.11
4  RESOLVED  com.paremus.util.types:1.0.11
5  ACTIVE    com.paremus.nimble.core:1.0.11
6  ACTIVE    com.paremus.nimble.repos:1.0.11
7  ACTIVE    com.paremus.nimble.cli:1.0.11
8  ACTIVE    com.paremus.util.cmds:1.0.11

Now let’s add the HelloDS bundle again, this time in the ACTIVE state.

% add HelloDS@active
Hello Declarative Services
% lsb
0  ACTIVE    org.eclipse.osgi:3.5.1.R35x_v20090827
1  ACTIVE    com.paremus.posh.runtime:1.0.11
2  ACTIVE    com.paremus.posh.shell:1.0.11
3  ACTIVE    com.paremus.posh.readline:1.0.11
4  RESOLVED  com.paremus.util.types:1.0.11
5  ACTIVE    com.paremus.nimble.core:1.0.11
6  ACTIVE    com.paremus.nimble.repos:1.0.11
7  ACTIVE    com.paremus.nimble.cli:1.0.11
8  ACTIVE    com.paremus.util.cmds:1.0.11
12  RESOLVED  javax.servlet:2.5.0.v200806031605
13  RESOLVED  osgi.cmpn:4.2.0.200908310645
14  ACTIVE    HelloDS:1.0.0
15  ACTIVE    org.eclipse.equinox.util:1.0.100.v20090520-1800
16  ACTIVE    org.eclipse.equinox.ds:1.1.1.R35x_v20090806

This time the HelloDS bundle has been activated. Not only that, but its ACTIVE state dependency on the DS extender has been met by bundle 16, org.eclipse.equinox.ds, which has also been activated. Note the “Hello Declarative Services” message that tells us everything is working.

Careful readers might be wondering why bundle 15 is also ACTIVE. This is a consequence of Eclipse’s treatment of bundle activation policies. Nimble didn’t activate it, but it does understand and track what’s going on.

As before we can get back to where we started by simply asking Nimble to remove the active state bundle

% remove HelloDS@active
Goodbye Declarative Services
[Pequod.local.0]% lsb
0  ACTIVE    org.eclipse.osgi:3.5.1.R35x_v20090827
1  ACTIVE    com.paremus.posh.runtime:1.0.11
2  ACTIVE    com.paremus.posh.shell:1.0.11
3  ACTIVE    com.paremus.posh.readline:1.0.11
4  RESOLVED  com.paremus.util.types:1.0.11
5  ACTIVE    com.paremus.nimble.core:1.0.11
6  ACTIVE    com.paremus.nimble.repos:1.0.11
7  ACTIVE    com.paremus.nimble.cli:1.0.11
8  ACTIVE    com.paremus.util.cmds:1.0.11

If you were using a resolver that wasn’t aware of ACTIVE bundle dependencies then you’d have to manage all of this yourself. With Nimble you just ask for what you need and let the resolver take care of the rest. This speeds up the development lifecycle and keeps redundant clutter out of your scripts.

There’s a lot more to Nimble’s resolver than what we’ve covered here. Its other features include policy based attachment of fragments and policy based resolution of optional dependencies. I’ll cover these and more in later posts.

Nimble is all about Making Modularity Manageable. If that’s what you want to do then try Nimble now.

Be Sociable, Share!