A little problem with the OSGi Version class

With the release of Indigo, Eclipse hackers have been exposed to a little change in the API of the org.osgi.framework.Version class. Specifically, a new method: compareTo(Version) has appeared. Prior to Eclipse 3.7, comparing two Version objects was done via the compareTo(Object) method.

This caused us in the Saros team to suffer a rather subtle bug. When Saros was compiled under Eclipse 3.6 it functioned as expected on the latest version of everyone's favourite IDE. But when Saros was compiled under 3.7 it suffered an NoSuchMethodException under 3.6 when a call to compareTo() was made. That's because it was compiled in an environment (Indigo) that inserted a call to compareTo(Version), but was run in an environment (Helios) that knew nothing about such a method.

The reverse situation (compiled in 3.6 and run under 3.7) still worked because a call was inserted that references compareTo(Object). Version inherits from the Comparable interface and thus recognises compareTo(Object) regardless of Eclipse version. I saw at least a couple of other projects complaining online about the same symptoms when I was seeking a solution. As I was unable to find a good solution, I thought I would share with you what I came up with.

To get around the problem, I used reflection. The solution searches first for compareTo(Version) and then for compareTo(Object). Whichever method the code finds, it then executes. Et voilà:  code that will run in any iteration of Eclipse regardless of the version it was compiled under... for now at least.

Here's the gist of the code:

Class versionClass = Version.class;
Method compareToMethod = null;
 
try {
    // Works on Eclipse 3.7
    compareToMethod = versionClass.getMethod("compareTo", Version.class);
} catch (NoSuchMethodException e1) {
    log.debug("We're on Eclipse 3.6 or earlier. Fall back compareTo(Object).");
    try {
        // Works on Eclipse 3.6 and earlier
        compareToMethod = versionClass.getMethod("compareTo", Object.class);
    } catch (NoSuchMethodException e2) {
        log.error("Unexpected error: cannot find compareTo() in Version" + e2);
    }
}
 
// Throws InvocationTargetException, IllegalAccessException
compareToMethod.invoke(v1, v2);

Edit: Thanks to a couple of readers pointing out the OSGi-preferred solution of compiling strictly against OSGi v1.5, which limits us to either: a) compiling strictly against Eclipse 3.6 (the solution we have been using), or b) importing version OSGi v1.5 into versions of Eclipse 3.7 and later.

The whole problem was actually slightly different from what I explained above. Remember, I'm not trying to use a brand new method newly introduced. What essentially happened was the signature of compareTo() method was changed (compareTo(Object) was actually removed without being depreciated between OSGi 1.5 and 1.6), causing formerly working code to become problematic. Of course, the OSGi method is safer, but prevents us from accessing functionality from later versions of the framework and required.

My solution above allows compilation against any version of Eclipse.

 

karl

 

7 thoughts on “A little problem with the OSGi Version class

  1. This seems like a dependency management problem on your part. You compiled against version 1.6 of the framework package, yet you don’t seem to import the framework package at version 1.6 (that is “[1.6,2.0)”).

    If you really want your code to run on a 1.5 framework, compile your code against the 1.5 framework package.

    What you describe above is just a hack to deal with one observed problem caused by your dependency management problem.

    1. I thought about this, but it didn’t strike me as a good solution. It’s not that we just want to run against OSGI 1.5, we want to run against ANY version of OSGI.

      By blanket insisting on a specific version I would think that leaves us open to the risk of incompatibilities in every place we reference OSGI. Isn’t it better to fix the known causes as they are found than introduce such a risk?

      1. Karl, BJ is correct and your objection is misplaced. This situation is precisely why OSGi uses semantic versions, and you should take advantage of that.

        First, in general terms you should never compile against a newer version of an API and then run against an older version. As soon as you compile against framework 1.6 (i.e., Eclipse 3.7), you run the very high risk of using API, advertently or inadvertently, that does not exist in framework 1.5 or lower. So you should always compile against the lowest version of an API that you can support.

        Second, if you compiled against OSGi framework 1.5 and used the version range [1.5,2.0) then you would not be limited to running only on 1.5. You would be able to run on any version from 1.5 up to (but not including) 2.0, which I believe that is exactly what you want.

        Incidentally if you were to develop using Bndtools then these version issues would be handled automatically. We use the lowest available version of the API during compilation, and generate a version range for the Import-Package that complies with standard OSGi version semantics.

Leave a Reply

Your email address will not be published. Required fields are marked *