Monday, June 27, 2011

Negative Qualifiers

Some face time with your colleagues can sometimes be really productive. Last week we had an EG meeting at IBM in my home town Montpellier. As I've known BJ Hargrave (IBM) and Richard Hall (Oracle/Apache Felix) for so many years I'd invited them to stay in our guest house. After arriving around noon we took the afternoon off to solve world hunger and climate change over a glass of rosé. As too often happens in these important discussions, we were seriously distracted with versioning strategies.

The OSGi version is defined as linearly increasing where the floor is the shortest form of the version. That is, 1.2.3 is less than any other version that starts with 1.2.3. A side effect of this model is that it is hard to model pre-release versions. A common approach is to use a snapshot version for artifacts that are on their way to becoming a release version. For example, 1.2.3-SNAPSHOT indicates that this artifact is becoming version 1.2.3 but is not there yet.

You can do this in OSGi with the qualifier but it is awkward because you want the final release version to be sort highest. Like 1.2.3.A1, 1.2.3.A2, 1.2.3.FINAL. The fact that you cannot just release 1.2.3 but have to use 1.2.3.FINAL generally offends our highly developed aesthetic sense.

The maven solution is to use a magic keyword: -SNAPSHOT. However, we felt a bit uncomfortable with a magic keyword. The qualifier in OSGi is left open for enterprises to implement a release strategy and it would be nice if they could do the same with a qualifier that came before the actual release. So why not have negative qualifiers?

The syntax for the OSGi qualifiers specifies a period as separate between the version and the qualifier, for example 1.2.3.qualifier.  If we allow a minus sign instead of the period we can interpret the qualifier as coming before the version instead of after. That is, 1.2.3-qualifier is actually less than 1.2.3. This contains the special case of -SNAPSHOT but enables the use of qualifiers to track build, patch, or other variations.

So what about ranges? Bundles that depend on artifacts normally specify a range like [1.2.3,1.2.4). I think this should include a version like 1.2.3-SNAPSHOT but there was some discussion about this, some wanted snapshot only when the range indicated snapshots, e.g.[1.2.3-,1.2.4-). For me this is awful because it requires rebuilding and retesting after you released. In think [1.2.3,1.2.4) must accept any negative qualifiers on 1.2.3. The discussion is ongoing.

Negative qualifiers would really help bndtools. We could always build with a 1.2.3-${tstamp} to capture the build time. If one day the artifact is released, we take the JAR and convert the minus in the version to a period. For example, the artifact has version 1.2.3-201106261020 if it comes from the development tree/repository. Once it is released, we change the manifest to version inside the release repository. No need to recompile or retest.

There are some loose ends with version ranges but I am sure we can work this out. However, the fact that got excited about this idea (world hunger has to wait another meeting) does of course not mean that the OSGi will start using this model tomorrow. BJ is working on an RFP to there is hope for the future. If you have any ideas, let us know.

Peter Kriens


  1. I'm not sure this is such a good idea. Very often, the manifest is contained in a jar. The jar may be signed as part of the build process. Changing the manifest would require a rebuild anyway in such a case. BWT, the qualifier is also in the jar name so the jar name also changes. As such, for many people it's a new artifact.

    We are technical and understand the qualifier and an impact caused by changing from dash to dot. However, for a lot people a rebuild and/or new artifact means re-test. That's the process. Best (in such scenarios) really is to not touch artifacts *after* final test and sign-off.

    Sounds like a lot additional complexity for aesthetic reasons.

  2. I think automatically signing a JAR during a continuous build is a bit of a red herring. I think that you can release as described in a way that you proof you do not introduce any changes. Time will tell.

    However, the negative qualifiers are also useful in many other scenarios.

  3. Why not only have negative qualifiers? Do we really need both?

    It would surely be a risk to change the semantics of the current qualifiers (even though we have the means for that now, by bumping the manifest version to 3) but I honestly don't see why we would need both positive and negative qualifiers.

    Make a choice, dump one!

  4. Interesting idea. However, for backward compatibility we still would need to interpret the old semantics so we still need a new character. I also like the idea that the release version has a time stamp, which would require a positive qualifier. So getting rid of the existing semantics is hard but I am sure the use will change.

  5. I think Marcel has a point.

    If you bump the manifest version there should be no problem; just do the old sorting on manifest versions lower than 3 and the new sorting on versions 3 and higher.

  6. Well, Bundle-ManifestVersion bumping is a pretty big step. Not likely that we'll do this for such a minor change. I also like the symmetry actually.

  7. Anything that allows OSGI versions to play a bit nicer with Maven versions will be greatly appreciated I think.

    Not using the 'magic keyword' is a good way to look at it too - as is the use of timestamps instead. I like it.

  8. Maven compatibility is a good point, though. Peter, do you know if the "dash" separator is compatible with Maven?

  9. If you use negative qualifiers and sort them in ascending order, you can still use timestamps for 'snapshots' and you do not need positive qualifiers anymore. The release obviously then always is the highest version, which makes sense. And I would break backward compatibility to do the right thing instead of having both, which to me is only confusing. The change is not compatible anyway as it breaks version implementation classes, probably included in every single framework.

  10. @marcel: Though I think you're scheme works well (and might even use it) I do not see why we should force everybody to change to that particular scheme?

  11. I think adding the negative qualifier is a great idea for compatibility reasons with Maven

    But, release management should be a different concern in my humble point of view

    Starting with the following requirements:
      - Upon a successful build allow for the Continuous Integration build to deploy the resulting artifact to a repository accessible to the team and to other CI builds (according to the criteria that will be specified latter)
      - Allow a person to promote an artifact or a group of artifacts through a predefined process:
          Development > Testing > Staging > Live
      - Don't setup things such that an artifact needs to be modified in any way after being tested

    One way of achieving the above could be:
      - Introduce, a new metadata to represent the artifact promotion level,
        - This information should not be stored inside the artifact, but it should rather be maintained in the repository
        - The promotion level should be updatable without the need to redeploy the artifact to the repository
        - The promotion levels could include a level for demoting artifacts like "Failed" or "Deprecated" which evaluates to be used for cases where the artifact didn't pass the formal testing phase
      - Add new syntax to allow choosing the minimum promotion level required while declaring a dependency range in a dependent artifact, examples syntax: "[1.2.3,1.2.4)@Testing" which would match an artifact that has been promoted for testing or higher

    Open Questions:
      - Should the promotion levels be standardized, or not?

    Summary: I don't think the qualifier should have anything to do with the status of the Artifact and whether it is released or not. The -SNAPSHOT convention in Maven is a pain point in my experience

  12. I am becoming more and more convinced that scoping the repository is the solution. As you said, the artifact should no longer be modified after it has been approved.

    A repo for released artifacts also allows artifacts to be removed. So maybe the status of an artifact can by definition not be reflected inside the artifact.